Developers · the integration contract, generated from the server
Build on ORBIS.ID.
A machine-readable contract at /openapi.json, quickstarts for the three roles you can play, and copy-pasteable calls for every step from provisioning an organization to checking a proof. Generated from the platform’s own route table, so it cannot describe a server that does not exist.
Building a product on this rail rather than reading the API? Partners is the path from “interested” to “verifying in production”: pick your role, see exactly what ORBIS needs and what the platform will prove before you go live. If you only need to check proofs, it will tell you that you need no account at all — see the relying-party path.
The contract
GET /openapi.json is an OpenAPI 3.1 document served
publicly and CORS-open, exactly like the platform’s other public artifacts. It is not written
by hand. A generator scans the dispatch code in
admin/admin-server.ts, admin/backoffice-api.ts, admin/directory.ts, admin/demo-bridge.ts, oid4vci/issuer-server.ts, oid4vp/verifier-server.ts, admin/partner-keys-routes.ts, admin/portal-routes.ts
and re-derives the route table; the build fails if the document and those files disagree.
operations documented
from the route table
distinct paths
public origin
response shapes deliberately not published
marked x-orbis-undocumented
source files scanned
scripts/gen-routes.ts
Read the honesty marker before you build
An operation carrying x-orbis-undocumented has a response shape ORBIS has
not promised. It is marked rather than guessed, because a contract that lies is worse
than no contract. Most of them are the Back Office data plane — the operator SPA’s own
view models, which change with the SPA. Integrate against the marked-and-specified operations,
and against the standards (OID4VCI metadata, status lists, did:web), which are
stable by definition.
Presence of an operation means only that this deployment mounts that route. It is not a claim that the capability behind it is commercially live — see live vs. planned.
What /v1 promises — and what it deliberately does not
ORBIS is two things at once, and they are versioned by opposite rules. Knowing which one you are calling is the single most useful thing on this page.
Verification — versioned by the standards, never by ORBIS
no /v1, ever
did:web documents, OID4VCI issuer metadata, OAuth authorization-server metadata,
Token Status Lists, the OID4VP request/response exchange and the published
conformance vectors are versioned by
OID4VCI, OID4VP, SD-JWT VC and Token Status List — the specifications
that define them. They stay at their standard, unversioned addresses forever, and they need
no account.
This is enforced, not intended. An ORBIS version segment anywhere on a verification path, or inside any address a verification artifact publishes, fails the ORBIS build. If ORBIS ever wanted to version the rail, it would have to delete the test that stops it.
Participation — versioned by ORBIS
/v1/…
Issuing, provisioning, depositing and the holder’s own wallet operations are ORBIS’s
API, so ORBIS versions them. An operation marked
x-orbis-stability: promised in /openapi.json is served
under /v1 and ORBIS owes you its shape.
The alias is additive. /v1/offers and /api/offers
are the same handler, the same auth, the same body, the same response — the router
rewrites the path and changes nothing else. No unversioned path is being retired,
and there is no date on which the paths you use today stop answering.
The admin plane — excluded, and free to change without notice
209 operations
The Back Office data plane (/api/admin/*) and every ORBIS product interface are
deliberately excluded from /v1. They are the operator
SPA’s own view models: their shape follows the screen, and it may change at any time,
without notice, without a deprecation window.
They are marked so you can tell. Every one carries x-orbis-internal and
x-orbis-undocumented in the document. Do not build on them. This is not a warning
that they are fragile today — it is a statement that ORBIS will not hold them still.
| Method | /v1 address | Also served at | Auth |
|---|---|---|---|
| POST | /v1/tenants | /api/tenants | operator |
| POST | /v1/types | /api/types | operator |
| POST | /v1/offers | /api/offers | operator |
| POST | /v1/tenants/{tenant}/bills | /api/tenants/{tenant}/bills | operator |
| GET | /v1/member/me | /member/me | holder |
| POST | /v1/wallet/{accountId}/items/{itemId}/accept | /api/wallet/{accountId}/items/{itemId}/accept | holder |
| POST | /v1/wallet/{accountId}/items/{itemId}/sharing | /api/wallet/{accountId}/items/{itemId}/sharing | holder |
Compatibility and deprecation
These terms bind ORBIS for every promised operation. They are published verbatim in
/openapi.json under x-orbis-versioning.policy, so your
tooling reads the same words you do.
What may change
A promised operation may gain OPTIONAL request fields and NEW response fields. It may never remove a field, repurpose one, change a field's type, tighten validation on an existing input, or change the status code an existing outcome returns.
What gets a new version
A breaking change gets a NEW prefix (/v2). /v1 is never changed in place. Two versions may be served side by side for as long as callers need both.
How something is retired
A promised operation is retired only after it is marked `deprecated: true` in /openapi.json with an `x-orbis-sunset` date, announced on /developers, and left answering for at least 180 days after that announcement. ORBIS does not shorten that window for its own convenience.
Your current URLs
Every existing unversioned path keeps answering exactly as it does today. /v1 is an ADDITIVE alias — the same handler, the same auth, the same body, the same response. There is no cutover and no date on which the current paths stop working.
Authentication planes
ORBIS has three credential planes and they never mix. A credential of one plane is not accepted on another, by construction — this is a product law, not a convention.
Operator bearer — the control plane
/api/*
Authorization: Bearer <token>. Three things produce one: an operator
session from POST /api/login, the Microsoft Entra bridge at
GET /auth/api-token, or a tenant-scoped operator token minted through the
provisioning ceremony (revealed exactly once — ORBIS stores only its hash and can never
show it to you again).
Roles are re-resolved on every request, from the operators table — a demoted, suspended or removed operator loses access on its very next call, not when a session expires. A tenant-scoped token may only act on its own organization; every mutation must name that organization explicitly, and a body with no organization fails closed.
Holder JWS — the wallet plane
typ orbis-holder+jwt
Authorization: Holder <jws>. A compact ES256 JWS signed by the device key,
typ orbis-holder+jwt, with the public JWK in the header, aud set to
this origin, an iat inside a five-minute freshness window, and a
jti that burns on first use. There is no shared secret on this plane.
The identity IS the key. A holder’s member id is the RFC 7638 thumbprint of that
JWK — the same pseudonymous id enrollment derives from a presentation’s
cnf.jwk, so “the key that holds the credential” and “the
member” are one thing. A path segment naming an account must match the signer, or the
request is refused with account_mismatch.
OID4VCI access token — one collection
/token → /credential
Issued by POST /token against a single-use pre-authorized code, and good for
exactly one credential collection. It authorises nothing else anywhere on the platform.
Everything under /verify/*, /manifest, /.well-known/* and
/status/* is public and CORS-open: a relying party verifies a
credential with no account, no key and no protocol code of its own.
Quickstarts
Pick the role you are playing. Each one names the smallest set of operations that gets you there.
Issuer organization
You want to hand out credentials people can carry.
POST /api/tenants— yourdid:webidentity and vault-held key.POST /api/types— declare the claims.POST /api/offers— a QR the wallet scans.POST /api/revoke— take it back; the signed status list is the public evidence.
Needs an operator bearer with the issuer role (or owner).
Relying party / verifier
You want to check a proof. You need no ORBIS account at all.
GET /verify/catalog— what can be asked for.POST /verify/request— bound to your nonce and audience.GET /verify/result/{transactionId}— poll the outcome, with theresultTokenthe request handed you. Still no account — the outcome is simply yours rather than the world’s.
Or skip the hosted flow entirely: resolve the issuer’s did:web, fetch its
status list, and run the chain yourself. Both routes check the same five links.
Wallet integrator
You are building a holder surface.
GET /manifest— white-label config per tenant.POST /token→POST /credential— the OID4VCI wire.GET /api/wallet/{accountId}/items— the bridge.POST /member/enroll,GET /member/me— membership.
Everything past enrollment is signed by the device key. Never a bearer secret.
The four-step flow, in curl
The same four steps /docs describes conceptually, as calls you can run.
Replace $ORBIS_OPERATOR_TOKEN with your own bearer; nothing below is a live
credential, and no example here is executed against the production origin.
# 1 · Provision an organization (owner bearer).
# DEMO-TENANT LAW: a demonstration org MUST be named so its slug starts
# with "demo-" and its display name ends with " (Demo)". Never un-marked.
curl -sS https://orbis.id/api/tenants \
-H 'authorization: Bearer $ORBIS_OPERATOR_TOKEN' \
-H 'content-type: application/json' \
-d '{"name":"Demo Acme Freight (Demo)"}'
# → {"slug":"demo-acme-freight-demo","name":"Demo Acme Freight (Demo)","did":"did:web:orbis.id:t:demo-acme-freight-demo"}
# 1b · Register a credential type for it.
# Claim specs: key | key:date | key:number | key:money | key:bool
# | key:array(text) | key:array(object) | key:enum(a|b|c)
# An unrecognised spec is a hard 400 — never a silently-dropped claim.
curl -sS https://orbis.id/api/types \
-H 'authorization: Bearer $ORBIS_OPERATOR_TOKEN' \
-H 'content-type: application/json' \
-d '{"tenant":"demo-acme-freight-demo","name":"Carrier Licence",
"claims":["carrier_name","licence_class:enum(A|B|C)","expires:date"]}'
# 2 · Create a credential offer. The offerUri is what you render as a QR.
curl -sS https://orbis.id/api/offers \
-H 'authorization: Bearer $ORBIS_OPERATOR_TOKEN' \
-H 'content-type: application/json' \
-d '{"tenant":"demo-acme-freight-demo","typeId":"carrier-licence",
"subject":{"carrier_name":"Acme Freight","licence_class":"B","expires":"2027-01-31"}}'
# → {"offerUri":"openid-credential-offer://..."}
# The WALLET then does this part — shown so you can see the whole wire:
curl -sS https://orbis.id/t/demo-acme-freight-demo/token \
-d 'grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code' \
-d 'pre-authorized_code=<from the offer>'
# → {"access_token":"...","c_nonce":"...","expires_in":300}
curl -sS https://orbis.id/t/demo-acme-freight-demo/credential \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
-d '{"credential_configuration_id":"carrier-licence",
"proof":{"proof_type":"jwt","jwt":"<ES256 JWT, typ openid4vci-proof+jwt, aud=this issuer, nonce=c_nonce>"}}'
# → {"credentials":[{"credential":"<SD-JWT VC>"}]}
# 3 · Hold. The credential lives in the holder's wallet under device-key
# custody. There is no ORBIS API that reads a holder's wallet: the wallet
# bridge is authenticated by the DEVICE KEY, and the account id IS the
# RFC 7638 thumbprint of that key. Nothing an operator holds can forge it.
curl -sS https://orbis.id/api/wallet/<thumbprint>/items
# → {"accountId":"...","fixtures":false,"items":[...]}
# "fixtures": true means this deployment is serving demonstration data.
# That marker is an honesty law, not a debug flag — never suppress it.
# This LIST is open (the wallet reads it with no header); every WRITE —
# accept, sharing — needs the device key's signature. Pending offers
# materialize only for an already-enrolled account, so reading this URL
# with an account id you invented can never make an issuer key sign.
# 4 · Verify — with NO ORBIS account, NO API key, from any origin.
curl -sS https://orbis.id/verify/catalog
# → {"types":[{"id":"...","name":"...","vct":"...","tenant":"...","claims":["..."]}]}
# Ask for exactly the claims you need, bound to YOUR nonce and audience.
curl -sS https://orbis.id/verify/request \
-H 'content-type: application/json' \
-d '{"vct":"<vct>","claims":["carrier_name"],
"nonce":"<your session nonce>","aud":"https://your-app.example"}'
# → {"requestUri":"openid4vp://?request_uri=...","transactionId":"...",
# "resultToken":"..."}
# Render requestUri as a QR — NEVER the resultToken. The wallet fetches the
# request object from https://orbis.id/oid4vp/request/<transactionId> and
# posts the presentation back to https://orbis.id/oid4vp/response.
# Poll the outcome. "outcome" stays null until the round-trip really completed
# — and until you present the resultToken, because the outcome is yours, not
# the world's. A poll without it reads null forever, exactly like an unknown
# transaction; nothing here ever asks you for an account.
curl -sS https://orbis.id/verify/result/<transactionId> \
-H 'authorization: Bearer <resultToken>'
# → {"outcome":{"ok":true,"presentation":{...},"presentedNonce":"...","presentedAud":"..."}}
# presentation.claims carries EXACTLY the claims you asked for. The holder's
# confirmation key is never served: its thumbprint is that holder's wallet
# account id.
# → {"outcome":{"ok":false,"error":"<which link of the chain failed>"}}
Typed claims are strict on purpose
number and money must arrive as JSON numbers — a numeric
string is refused, never coerced. money is a whole number of minor units
and a fractional value is refused outright, because silently rounding money is the worst
failure available. Sign is not constrained: a credit or a refund is legitimately negative.
bool must be a JSON boolean; enum must be one of the registered
options; array(text) and array(object) make each element individually
disclosable. A claim key may not collide with a reserved SD-JWT member
(iss, sub, aud, exp, nbf,
iat, jti, cnf, vct, status,
_sd, _sd_alg) — overwriting cnf would break holder
binding and overwriting status would break revocation.
Verify it yourself — the open rail
ORBIS is a public trust rail, not a platform with an API. The measure of that is not how many customers integrate: it is whether a credential ORBIS issued can still be verified by a stranger — with no ORBIS account, no ORBIS code and no ORBIS permission, ideally even when ORBIS is unreachable. Everything in this section exists to make that testable rather than promised.
The SDK is optional. The vectors are the contract.
Publishing an SDK alone would make ORBIS a soft chokepoint: everyone would run ORBIS’s code, so ORBIS’s bugs would quietly become the specification. Publishing an SDK plus conformance vectors makes ORBIS a spec. Write your verifier in Rust, Go, Java or anything else; if it passes the vectors it is as correct as ours, and you never asked us anything to find that out.
The library, if you want it
@orbis-id/verifier is a zero-dependency verifier that runs in Node and
in a browser and performs no I/O at all — you fetch the artifacts, it checks
them. That is what makes verification keep working when ORBIS is down.
Published It is on npm as @orbis-id/verifier under Apache-2.0, and the conformance runner can be used without installing anything: npx orbis-conformance --cmd <your-verifier>.
// Verify an ORBIS credential offline. Zero dependencies, Node and browser.
// Using this package is OPTIONAL: the conformance vectors are the contract.
import { verifyPresentation } from '@orbis-id/verifier';
const didDocument = await (await fetch('https://orbis.id/.well-known/did.json')).json();
const token = await (await fetch('https://orbis.id/status/1')).text();
const outcome = await verifyPresentation({
presentation, // the vp_token you received
didDocument, // the issuer's published did:web document
statusList: { uri: 'https://orbis.id/status/1', token },
expected: { audience: 'https://your-app.example', nonce: yourNonce },
});
if (outcome.verdict === 'accept') use(outcome.claims);
// THREE verdicts, not two. "indeterminate" means the check could not be made —
// the credential names a status list and you supplied none, OR it carries no
// "status" claim at all and so declares no revocation mechanism to read. Both
// answer the same way: a credential whose revocation state was never read is
// never accepted. It is never an accept, and treating it as a reject is YOUR
// policy decision to make out loud.
// outcome.checks is the full ledger: ten links, each with its own status
// (passed | failed | not_applicable | not_performed). The verdict is derived
// from that ledger, so a check that did not run can never be reported as one
// that passed.
It never silently passes a check it did not perform
A verifier that reports “valid” for a credential whose revocation state it could not
read is worse than one that reports nothing. So the result is not a boolean: every one of the ten
links carries its own status, and the verdict is derived from that ledger. A missing
input yields indeterminate — a third answer you must handle deliberately.
One limitation is stated rather than hidden: only top-level named disclosures are
checked. A credential using nested or array-element _sd is refused with
unsupported_disclosure_structure, never partially verified and reported as accepted.
Self-certify against the published vectors
GET /conformance/vectors is public, CORS-open and
requires no credential — permanently, and enforced by a test rather than by intent. It
publishes sixteen self-describing vectors: a presentation, the issuer’s
did:web document and a signed status list, plus what each one proves and the
verdict a correct verifier MUST reach. Everything is inert data: run them with the network switched
off.
# 1 · Fetch the vectors. No account, no key, no permission. CORS-open.
curl -sS https://orbis.id/conformance/vectors > vectors.json
# 2 · Point the runner at YOUR implementation, in any language.
npx orbis-conformance --vectors ./vectors.json --cmd './target/release/my-verifier'
# Your command is spawned once and speaks newline-delimited JSON:
# -> stdin {"id":"valid","input":{ ...the vector's `input` object verbatim... }}
# <- stdout {"id":"valid","verdict":"accept","failure":null,"claims":{...}}
# "verdict" is accept | reject | indeterminate and is the only required field
# besides "id". Exit code 0 when every vector passes, 1 otherwise.
# 3 · Read the report.
# level 1 (verdict) 16/16 CONFORMANT
# level 2 (verdict+reason) 16/16 CONFORMANT
# Level 1 is the BINDING contract: the verdict alone. Level 2 also matches the
# failure code and the disclosed claims — recommended, never required. Forcing
# you to adopt ORBIS's enum names would be the chokepoint this exists to prevent.
| Vector | Required verdict | What a correct answer proves |
|---|---|---|
| valid | accept | The whole chain is walked; exactly the disclosed claims are readable and the withheld one never appears. |
| revoked | reject | Revocation is really read from the signed list. Signature and holder binding are still sound. |
| status-list-withheld | indeterminate | An unanswerable question is not an accept. Catches a verifier that fails open when it cannot reach the list. |
| tampered-issuer-signature | reject | The issuer signature is verified, not merely parsed. The key binding is re-signed so nothing else can catch it by accident. |
| wrong-issuer-key | reject | The key is bound by the signature, not by the identifier — and the status list is signed by the same wrong key, so no incidental catch remains. |
| stolen-credential | reject | Holder binding is enforced. Without it a credential works for whoever copies it. |
| wrong-credential-type | reject | The vct you demanded is actually compared. |
| required-claim-withheld | reject | A claim the holder withheld is genuinely absent, and the verifier says so instead of proceeding. |
| did-document-not-bound | reject | The document is bound to the identifier it claims; otherwise any host could publish keys for any issuer. |
| forged-disclosure | reject | A holder cannot invent a claim the issuer never signed, even with a perfectly valid key binding. |
| disclosure-set-altered | reject | sd_hash is checked, so the disclosure set cannot be edited in transit. |
| audience-mismatch | reject | A presentation meant for another relying party is refused. |
| nonce-replay | reject | A previously captured presentation cannot answer a fresh challenge. |
| expired-status-list | reject | A stale revocation answer is not an answer. |
| status-list-substituted | reject | The list is bound to the URI the credential names — otherwise swapping which list is served un-revokes everything. |
| malformed-presentation | reject | Garbage gets a definite verdict rather than a crash or a coerced decision. |
The vectors are minted, not committed — and that is deliberate
A signed SD-JWT VC and a statuslist+jwt are high-entropy eyJ…
strings that a secret scanner matches on sight (measured: four jwt findings on an
emitted set), and ORBIS reserves its scanner allowlist for verified false positives. So the
repository holds a deterministic recipe and the bytes are produced on demand.
Never pin the bytes. schema versions the shape,
setVersion versions the contract, and digest names the exact bytes a
given conformance report was run against. There is no PII, only RFC 6761 .example
hosts, and no JWK private scalar anywhere.
The vectors are mutation-tested: a second, fully independent implementation is broken twelve different ways — skipping the issuer signature, skipping revocation, failing open, ignoring the audience, and so on — and every one must be caught at conformance level 1. A vector set that cannot fail a broken verifier would prove nothing, so that is a build gate rather than a claim.
The verification surface is not versioned by ORBIS. There is no /v1 on
any trust artifact and there never will be: did:web, OID4VCI metadata, SD-JWT VC and
Token Status List are versioned by their own standards, and the vectors by their own
setVersion. The participation plane is where ORBIS versioning
belongs.
Is orbis.id actually running that code?
Open code proves the design. It does not prove that the machine answering this request is
running it — and that is the step most “open source for credibility” stories skip.
GET /build is where ORBIS stops asking to be taken at its word:
it names the commit this deployment was built from, and it recomputes the digest of its own
files on disk rather than reciting a string someone typed at build time.
# 1 · Ask the running instance what it is. No account, no key.
curl -s https://orbis.id/build
# {
# "commit": "<40-hex>", the commit this artifact was built from
# "sourceDigest": "sha256:...", the content digest recorded AT BUILD TIME
# "observedSourceDigest": "sha256:...", recomputed from the files on disk NOW
# "matches": true|false,
# "divergence": { "missing": 0, "changed": 0, "extra": 0 },
# "attestation": { "available": false, "reason": "..." }
# }
#
# Read "attestation" FIRST. It says whether anything outside this server signs
# the commit above, and today the honest answer is no.
# 2 · Recompute the digest from the commit. Needs no ORBIS software:
# coreutils is the entire trust base.
git checkout <commit-from-step-1>
LC_ALL=C find .orbis package.json package-lock.json tsconfig.json scripts src \
-type f ! -name '*.test.ts' ! -name '.DS_Store' \
| LC_ALL=C sort | xargs -n1 sha256sum | sha256sum
# The output must equal "sourceDigest". It is a sha256 over the sorted
# "<sha256> <path>" lines of every file the deploy artifact contains — so it
# is unaffected by mtimes, file order, permissions or which zip built it.
# 3 · Rebuild the deploy artifact itself, byte for byte.
npx tsx scripts/build-deploy-zip.ts --out deploy.zip
sha256sum deploy.zip
# Must equal the archive digest printed in that release's deploy run.
# The builder refuses a working tree that is not the commit, normalises every
# entry's timestamp to the commit's own, and drops the extra fields that record
# the building machine's access times and uid/gid. What it cannot control is
# the DEFLATE implementation: Info-ZIP 3.0 is what GitHub's runners and macOS
# ship, and a different compressor may produce a different archive digest for
# an identical source tree. That is why step 2, and not this step, is the one
# that decides.
# 4 · Check who signed it — and today, find out that nobody did.
gh attestation verify deploy.zip --repo Jonatans11/OID
# GitHub refuses to create artifact attestations for user-owned PRIVATE
# repositories. Measured, not assumed:
#
# POST /repos/Jonatans11/OID/attestations
# 422 "Feature not available for user-owned private repositories.
# To enable this feature, please make this repository public."
#
# The deploy workflow already carries a fully configured attest step, gated on
# that one fact. Until it lifts, steps 1-3 tie the running bytes to a commit;
# nothing ties them to GitHub's word that GitHub built them.
Exactly where this chain still breaks
A verification recipe that overstates itself is worth less than none, so the gaps are named rather than left for you to find.
-
Nothing signs the artifact yet. GitHub will not attest builds from a
user-owned private repository, so today
/build’s commit is a claim by the server — checkable against the source, not signed by a third party. Theattestationfield says so on every response. -
The repository is private. Steps 2 and 3 need the source, so today they are
checks an auditor with access can run rather than ones an anonymous stranger can. What is
published today is
@orbis-id/verifierand the sixteen conformance vectors above; the server is not. -
Dependencies are installed on the host, not shipped. The artifact carries
package-lock.json— which the digest covers — and the platform installs from it on the App Service. The digest therefore proves which dependency graph was requested, not which bytes the registry served. -
A deployment can be a superset of its commit. The platform is deployed by
overlaying a zip onto the running directory, which never removes what is already there. That
is what
divergence.extracounts, and it is a real number on a real system, not a hypothetical.
Webhooks
ORBIS delivers events by HTTP POST with an HMAC-SHA256 signature over the raw body. Endpoints are
registered and their secrets rotated from the Back Office integration desk
(/api/admin/integration/webhooks); deliveries are retried and dead-lettered, and the
destination URL is re-validated against the SSRF allowlist on every attempt, not just at
registration — a URL can outlive a topology change.
// Verify an ORBIS webhook. Compare bytes, never strings, and never trust
// an unsigned body. Headers ORBIS sends:
// x-orbis-signature: sha256=<hex> HMAC-SHA256 of the RAW body, hex
// x-orbis-event: <event type>
// x-orbis-delivery: <delivery id> (idempotency key — dedupe on this)
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyOrbisWebhook(rawBody: string, header: string | null, secret: string): boolean {
if (!header) return false;
const m = /^sha256=([0-9a-f]{64})$/i.exec(header.trim());
if (!m) return false;
const expected = Buffer.from(createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex'), 'hex');
const given = Buffer.from(m[1].toLowerCase(), 'hex');
return expected.length === given.length && timingSafeEqual(expected, given);
}
// Read the RAW body. If your framework has already parsed and re-serialised
// the JSON, the bytes have changed and the signature will never match.
Three ways to get this wrong
- Comparing strings. Use a constant-time comparison over decoded bytes, as above.
- Signing a re-serialised body. The HMAC is over the bytes ORBIS sent. Capture the raw body before any JSON middleware touches it.
- Not deduplicating. Retries are real.
x-orbis-deliveryis your idempotency key.
A redirect is never followed on delivery: a 3xx is recorded as a failure rather than chased to a host the allowlist did not approve.
Sandbox & the demo-tenant law
There is no separate sandbox host. You experiment inside the demo namespace, and that namespace is marked — permanently, in code.
The law
- A demonstration organization’s slug starts with
demo-. - Its display name ends with “ (Demo)”.
- A demo tenant is never un-marked — not in the console, not in the wallet, not in an export.
- Fixture data always answers
fixtures: trueon the wallet bridge. That flag is an honesty marker, not a debug switch. - Real organizations are created only through the provisioning ceremony, never by editing a demo one into place.
The platform enforces the namespace rather than trusting it: the off-boarding and demo-hygiene
surfaces select tenants by that exact predicate (demo- prefix or an explicit demo
marker on the profile), so a sweep can never take a real organization with it — and can never
miss a demo one that was renamed to look real.
Nothing on this page has been executed against the production origin, and no
example value here is live data. When you run the quickstarts, run them against a
demo- organization first.
Live vs. planned — what you can actually integrate with today
The table below is about the integration surface, not about the business. Every row is stated at the strength the platform’s own records support it, and a route being mounted is never by itself evidence that a capability is in production use.
| Surface | State | What that means for you |
|---|---|---|
Public trust artifacts — did:web, OID4VCI metadata, status lists, /manifest |
Live | Standards-defined and CORS-open. Safe to build against; verified in this repository’s conformance suite. |
Issuance over OID4VCI — /token, /credential |
Live | The wire is the specification. Shapes above are read off the server source. |
Hosted verification — /verify/*, /oid4vp/* |
Live | Zero-account verification with caller-supplied nonce and audience binding. |
Tenant provisioning & credential types — /api/tenants, /api/types, /api/offers |
Live | Operator bearer required. Request shapes are specified above; several success bodies are console view models and are marked accordingly. |
Wallet bridge — /api/wallet/*, /member/* |
Live | Holder-signed. The item wire shape is pinned by the demo-bridge contract document, which is the authority — not this page. |
Deposit rail — POST /api/tenants/{tenant}/bills |
Built & mounted | Request and response shapes are contracted (contracts 0.3.0) and specified above. Whether a given deployment has an external caller is a deployment fact, not an API fact. |
| Webhook event plane | Built & mounted | HMAC-signed, retried, dead-lettered, SSRF-guarded. Register endpoints from the Back Office; the verification recipe above is the whole contract. |
Holder directory — /directory/* |
Conditional | Answers 503 directory_unconfigured unless the deployment is configured with a directory pepper. Its shapes live in the directory contract document, not here. |
Back Office data plane — /api/admin/* |
Internal | Mounted and role-gated, and the roles are published here. The response shapes are not a partner contract and are marked x-orbis-undocumented. Do not build against them. |
Harness admin — /harness/* |
Not on this image | Compile-gated to the test-harness build and absent from production, so it is deliberately excluded from the document rather than described as if it existed. |
| Partner self-serve pages — /partners, the four profile paths, apply, status | Live | Served by this deployment. The profiles and the eight-state pipeline are rendered from the platform’s own definitions, so they cannot drift from the operator desk’s. |
Partner application intake — POST /api/partners/applications (public) |
Live | Public and unauthenticated, and it records into the operator Partners desk’s own pipeline — the same record, the same state machine, the same history an operator-typed application gets. It can create stage applied and nothing else: no tenant, token, scope or stage is nameable from it. Bounded, strict-schema and rate-limited per caller; not CORS-open. |
| Certification readiness board for partners | Built & mounted | The engine runs. It is reached as the four-eyes action partner.certify-run from the operator Partners desk — not as a partner-callable route, so there is nothing here for you to integrate against. A run produces a recorded verdict per exercise; the readiness board reads the latest run. Exercises that need an effect this deployment cannot supply report could not run, never a pass. |
Conformance vectors — GET /conformance/vectors |
Live | Public, CORS-open, no credential ever. Sixteen vectors, mutation-tested against twelve broken verifiers. See the open rail. The bytes are minted per process and must not be pinned; the contract is setVersion plus each vector’s required verdict. |
Verifier SDK — @orbis-id/verifier |
Published | It is on npm as @orbis-id/verifier under Apache-2.0, and the conformance runner can be used without installing anything: npx orbis-conformance --cmd <your-verifier>. Zero dependencies, Node and browser, no I/O. Using it remains optional: the vectors are the contract, and a verifier that passes them is conformant whatever it is written in. Verified against this deployment’s live vector set at publication — 16/16 at level 1 and level 2. |
Per-key rate limits and RateLimit headers |
Live | Participation plane only. A request authenticated by an operator key is metered against that key’s declared rate class and answers the IETF standard fields (RateLimit-Policy, RateLimit, and Retry-After on a 429) — not proprietary X-Orbis-* names. Budgets belong to keys, never to source addresses. The verification plane is never metered: no identity, no per-key accounting, no upsell — see the open rail. Human console sessions carry no quota. |
The /v1 prefix |
Partial | 7 participation operations answer under /v1 today, each an additive alias of its unversioned path: same handler, same auth, same body. The set is rendered from public-api-v1.ts, never typed here, so this row cannot drift from what the router serves. It does not yet cover the whole participation plane, and no unversioned path is being retired. |
| Self-service API keys | Partial | All three phases are built and the routes are mounted: a partner signs in with a code sent to the contact email on their approved application, then mints, lists, rotates and revokes keys inside the grant an operator attached at go-live. The tenant comes from that stored grant and is never read from the request, so a key cannot be pointed at another customer. What is still missing is the customer record the grant should hang off — grants are keyed by application reference today — and the portal screens a partner would use, so in practice an operator still attaches the grant before any of this is reachable for a given partner. |
| Issuer and wallet SDKs | Not built | Only the verifier SDK is published. There is no library for issuing a credential and none for holding one, so both of those are yours to write against the wire formats above. |
What this page does not claim
- No throughput, uptime, latency or customer numbers — none are measured here.
- No promise that an
x-orbis-undocumentedshape will stay as you find it. - No SLA. No versioning guarantee beyond the document’s own
info.version. - The wallet is web-only; there are no native app-store builds, and account recovery is not implemented.
Reference
Every operation this deployment mounts, grouped as the document groups them. Paths are as served on the public origin. For request and response detail, read /openapi.json — this table and that document are generated from the same route inventory in the same build.
Human surfaces (HTML) 22
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| GET | / | Public front page | public | — |
| GET | /admin | Back Office | Entra session | — |
| GET | /capabilities | Public capability register | public | — |
| GET | /console | Identity Console | Entra session | — |
| GET | /demo | Investor-demo console | public | — |
| GET | /developers | Developer portal | public | — |
| GET | /docs | How ORBIS.ID works | public | — |
| GET | /for-business | For Business — audience page | public | — |
| GET | /for-cities | For Cities — audience page | public | — |
| GET | /for-government | For Government — audience page | public | — |
| GET | /for-people | For People — audience page | public | — |
| GET | /openapi.json | This document | public | — |
| GET | /partners | Partners — start integrating | public | — |
| GET | /partners/apply | Partner application form | public | — |
| GET | /partners/data-partner | Partner path — data partner | public | — |
| GET | /partners/issuer | Partner path — issuer | public | — |
| GET | /partners/relying-party | Partner path — relying party / verifier | public | — |
| GET | /partners/status | Partner application status lookup | public | — |
| GET | /partners/wallet | Partner path — wallet | public | — |
| GET | /regulations | Regulations and GDPR operations | public | — |
| GET | /standards | Standards and conformance | public | — |
| GET | /verify | Hosted verification page | public | — |
Trust artifacts 9
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| GET | /.well-known/did.json | Platform did:web document | public | shape not published |
| GET | /.well-known/security.txt | Security contact (RFC 9116) | public | shape not published |
| GET | /build | Build provenance of the running instance | public | — |
| GET | /conformance/vectors | Open Rail conformance vector set | public | — |
| GET | /manifest | Tenant manifest (wallet white-label config) | public | — |
| GET | /status/{seq} | Signed status list by sequence (platform issuer) | public | — |
| GET | /status/1 | Signed status list (list 1, live) | public | — |
| GET | /t/{tenant}/did.json | Tenant did:web document | public | shape not published |
| GET | /t/{tenant}/status/{seq} | Signed status list by sequence (tenant issuer) | public | — |
Issuance — OID4VCI 7
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| GET | /.well-known/oauth-authorization-server | OAuth 2.0 authorization server metadata (RFC 8414) | public | — |
| GET | /.well-known/openid-credential-issuer | OID4VCI credential issuer metadata | public | — |
| POST | /api/offers | Create a credential offer | operator bearer | — |
| POST | /api/revoke | Revoke an issued credential | operator bearer | shape not published |
| POST | /credential | Collect the credential | OID4VCI token | — |
| POST | /token | Redeem a pre-authorized code for an access token | public | — |
| POST | /v1/offers | Create a credential offer | operator bearer | — |
Back office 136
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| GET | /api/admin/activity | GET /api/admin/activity | operator bearer | shape not published internal |
| GET | /api/admin/analytics | GET /api/admin/analytics | operator bearer | shape not published internal |
| GET | /api/admin/approvals | GET /api/admin/approvals | operator bearer | shape not published internal |
| POST | /api/admin/approvals | POST /api/admin/approvals | operator bearer | shape not published internal |
| GET | /api/admin/approvals/{approvalId} | GET /api/admin/approvals/{approvalId} | operator bearer | shape not published internal |
| POST | /api/admin/approvals/{approvalId}/approve | POST /api/admin/approvals/{approvalId}/approve | operator bearer | shape not published internal |
| POST | /api/admin/approvals/{approvalId}/reject | POST /api/admin/approvals/{approvalId}/reject | operator bearer | shape not published internal |
| POST | /api/admin/approvals/{approvalId}/withdraw | POST /api/admin/approvals/{approvalId}/withdraw | operator bearer | shape not published internal |
| GET | /api/admin/audit | GET /api/admin/audit | operator bearer | shape not published internal |
| GET | /api/admin/audit/{seq} | GET /api/admin/audit/{seq} | operator bearer | shape not published internal |
| POST | /api/admin/compliance/accountability/attest | POST /api/admin/compliance/accountability/attest | operator bearer | shape not published internal |
| GET | /api/admin/compliance/accountability/attestations | GET /api/admin/compliance/accountability/attestations | operator bearer | shape not published internal |
| GET | /api/admin/compliance/accountability/role-history | GET /api/admin/compliance/accountability/role-history | operator bearer | shape not published internal |
| GET | /api/admin/compliance/accountability/role-separation | GET /api/admin/compliance/accountability/role-separation | operator bearer | shape not published internal |
| POST | /api/admin/compliance/dsar/export | POST /api/admin/compliance/dsar/export | operator bearer | shape not published internal |
| POST | /api/admin/compliance/dsar/lookup | POST /api/admin/compliance/dsar/lookup | operator bearer | shape not published internal |
| POST | /api/admin/compliance/dsar/negative-response | POST /api/admin/compliance/dsar/negative-response | operator bearer | shape not published internal |
| GET | /api/admin/compliance/dsar/requests | GET /api/admin/compliance/dsar/requests | operator bearer | shape not published internal |
| GET | /api/admin/compliance/erasure/requests | GET /api/admin/compliance/erasure/requests | operator bearer | shape not published internal |
| POST | /api/admin/compliance/erasure/requests | POST /api/admin/compliance/erasure/requests | operator bearer | shape not published internal |
| GET | /api/admin/compliance/erasure/requests/{requestId} | GET /api/admin/compliance/erasure/requests/{requestId} | operator bearer | shape not published internal |
| POST | /api/admin/compliance/erasure/requests/{requestId}/refuse | POST /api/admin/compliance/erasure/requests/{requestId}/refuse | operator bearer | shape not published internal |
| POST | /api/admin/compliance/erasure/requests/{requestId}/verify | POST /api/admin/compliance/erasure/requests/{requestId}/verify | operator bearer | shape not published internal |
| GET | /api/admin/compliance/incidents | GET /api/admin/compliance/incidents | operator bearer | shape not published internal |
| POST | /api/admin/compliance/incidents | POST /api/admin/compliance/incidents | operator bearer | shape not published internal |
| GET | /api/admin/compliance/incidents/{incidentId} | GET /api/admin/compliance/incidents/{incidentId} | operator bearer | shape not published internal |
| POST | /api/admin/compliance/incidents/{incidentId} | POST /api/admin/compliance/incidents/{incidentId} | operator bearer | shape not published internal |
| GET | /api/admin/compliance/incidents/{incidentId}/affected-scope | GET /api/admin/compliance/incidents/{incidentId}/affected-scope | operator bearer | shape not published internal |
| GET | /api/admin/compliance/meta-inventory | GET /api/admin/compliance/meta-inventory | operator bearer | shape not published internal |
| GET | /api/admin/compliance/program | GET /api/admin/compliance/program | operator bearer | shape not published internal |
| POST | /api/admin/compliance/retention/purge-run | POST /api/admin/compliance/retention/purge-run | operator bearer | shape not published internal |
| GET | /api/admin/compliance/retention/rules | GET /api/admin/compliance/retention/rules | operator bearer | shape not published internal |
| POST | /api/admin/compliance/retention/rules | POST /api/admin/compliance/retention/rules | operator bearer | shape not published internal |
| GET | /api/admin/compliance/ropa | GET /api/admin/compliance/ropa | operator bearer | shape not published internal |
| GET | /api/admin/compliance/ropa/purposes | GET /api/admin/compliance/ropa/purposes | operator bearer | shape not published internal |
| POST | /api/admin/compliance/ropa/type-purpose | POST /api/admin/compliance/ropa/type-purpose | operator bearer | shape not published internal |
| GET | /api/admin/credential | GET /api/admin/credential | operator bearer | shape not published internal |
| GET | /api/admin/credential-type | GET /api/admin/credential-type | operator bearer | shape not published internal |
| GET | /api/admin/credential-types | GET /api/admin/credential-types | operator bearer | shape not published internal |
| GET | /api/admin/credentials | GET /api/admin/credentials | operator bearer | shape not published internal |
| GET | /api/admin/holder | GET /api/admin/holder | operator bearer | shape not published internal |
| GET | /api/admin/holders | GET /api/admin/holders | operator bearer | shape not published internal |
| GET | /api/admin/integration/contracts | GET /api/admin/integration/contracts | operator bearer | shape not published internal |
| POST | /api/admin/integration/contracts/handshake | POST /api/admin/integration/contracts/handshake | operator bearer | shape not published internal |
| GET | /api/admin/integration/deliveries | GET /api/admin/integration/deliveries | operator bearer | shape not published internal |
| GET | /api/admin/integration/deliveries/{deliveryId} | GET /api/admin/integration/deliveries/{deliveryId} | operator bearer | shape not published internal |
| POST | /api/admin/integration/deliveries/{deliveryId}/redrive | POST /api/admin/integration/deliveries/{deliveryId}/redrive | operator bearer | shape not published internal |
| GET | /api/admin/integration/deposits | GET /api/admin/integration/deposits | operator bearer | shape not published internal |
| GET | /api/admin/integration/deposits/{depositId} | GET /api/admin/integration/deposits/{depositId} | operator bearer | shape not published internal |
| GET | /api/admin/integration/deposits/integrity | GET /api/admin/integration/deposits/integrity | operator bearer | shape not published internal |
| GET | /api/admin/integration/key-options | GET /api/admin/integration/key-options | operator bearer | shape not published internal |
| GET | /api/admin/integration/provisioning | GET /api/admin/integration/provisioning | operator bearer | shape not published internal |
| POST | /api/admin/integration/provisioning | POST /api/admin/integration/provisioning | operator bearer | shape not published internal |
| GET | /api/admin/integration/provisioning/{requestId} | GET /api/admin/integration/provisioning/{requestId} | operator bearer | shape not published internal |
| POST | /api/admin/integration/provisioning/{requestId}/reject | POST /api/admin/integration/provisioning/{requestId}/reject | operator bearer | shape not published internal |
| GET | /api/admin/integration/tokens | GET /api/admin/integration/tokens | operator bearer | shape not published internal |
| POST | /api/admin/integration/tokens | POST /api/admin/integration/tokens | operator bearer | shape not published internal |
| POST | /api/admin/integration/tokens/{tokenId}/revoke | POST /api/admin/integration/tokens/{tokenId}/revoke | operator bearer | shape not published internal |
| POST | /api/admin/integration/tokens/{tokenId}/rotate | POST /api/admin/integration/tokens/{tokenId}/rotate | operator bearer | shape not published internal |
| GET | /api/admin/integration/webhooks | GET /api/admin/integration/webhooks | operator bearer | shape not published internal |
| POST | /api/admin/integration/webhooks | POST /api/admin/integration/webhooks | operator bearer | shape not published internal |
| GET | /api/admin/integration/webhooks/{webhookId} | GET /api/admin/integration/webhooks/{webhookId} | operator bearer | shape not published internal |
| POST | /api/admin/integration/webhooks/{webhookId}/active | POST /api/admin/integration/webhooks/{webhookId}/active | operator bearer | shape not published internal |
| POST | /api/admin/integration/webhooks/{webhookId}/rotate-secret | POST /api/admin/integration/webhooks/{webhookId}/rotate-secret | operator bearer | shape not published internal |
| GET | /api/admin/integration/webhooks/coverage | GET /api/admin/integration/webhooks/coverage | operator bearer | shape not published internal |
| GET | /api/admin/me | GET /api/admin/me | operator bearer | shape not published internal |
| GET | /api/admin/offboarding | GET /api/admin/offboarding | operator bearer | shape not published internal |
| GET | /api/admin/ops/audit | GET /api/admin/ops/audit | operator bearer | shape not published internal |
| GET | /api/admin/ops/azure | GET /api/admin/ops/azure | operator bearer | shape not published internal |
| POST | /api/admin/ops/backup | POST /api/admin/ops/backup | operator bearer | shape not published internal |
| GET | /api/admin/ops/backup-drills | GET /api/admin/ops/backup-drills | operator bearer | shape not published internal |
| GET | /api/admin/ops/backups | GET /api/admin/ops/backups | operator bearer | shape not published internal |
| GET | /api/admin/ops/comms-health | GET /api/admin/ops/comms-health | operator bearer | shape not published internal |
| GET | /api/admin/ops/demo-hygiene | GET /api/admin/ops/demo-hygiene | operator bearer | shape not published internal |
| GET | /api/admin/ops/did-drift | GET /api/admin/ops/did-drift | operator bearer | shape not published internal |
| GET | /api/admin/ops/expiry | GET /api/admin/ops/expiry | operator bearer | shape not published internal |
| POST | /api/admin/ops/expiry-dates | POST /api/admin/ops/expiry-dates | operator bearer | shape not published internal |
| POST | /api/admin/ops/fixtures | POST /api/admin/ops/fixtures | operator bearer | shape not published internal |
| GET | /api/admin/ops/health | GET /api/admin/ops/health | operator bearer | shape not published internal |
| GET | /api/admin/ops/keys | GET /api/admin/ops/keys | operator bearer | shape not published internal |
| GET | /api/admin/ops/revocations | GET /api/admin/ops/revocations | operator bearer | shape not published internal |
| POST | /api/admin/ops/revoke | POST /api/admin/ops/revoke | operator bearer | shape not published internal |
| GET | /api/admin/ops/rotation | GET /api/admin/ops/rotation | operator bearer | shape not published internal |
| GET | /api/admin/ops/status-integrity | GET /api/admin/ops/status-integrity | operator bearer | shape not published internal |
| GET | /api/admin/ops/status-lists | GET /api/admin/ops/status-lists | operator bearer | shape not published internal |
| GET | /api/admin/ops/status-lists/{issuerDid} | GET /api/admin/ops/status-lists/{issuerDid} | operator bearer | shape not published internal |
| GET | /api/admin/ops/stuck-transactions | GET /api/admin/ops/stuck-transactions | operator bearer | shape not published internal |
| GET | /api/admin/ops/trust | GET /api/admin/ops/trust | operator bearer | shape not published internal |
| POST | /api/admin/ops/trust/add | POST /api/admin/ops/trust/add | operator bearer | shape not published internal |
| POST | /api/admin/ops/trust/restore | POST /api/admin/ops/trust/restore | operator bearer | shape not published internal |
| POST | /api/admin/ops/trust/suspend | POST /api/admin/ops/trust/suspend | operator bearer | shape not published internal |
| GET | /api/admin/ops/wire-funnels | GET /api/admin/ops/wire-funnels | operator bearer | shape not published internal |
| GET | /api/admin/orgs | GET /api/admin/orgs | operator bearer | shape not published internal |
| POST | /api/admin/orgs | POST /api/admin/orgs | operator bearer | shape not published internal |
| GET | /api/admin/orgs/{slug} | GET /api/admin/orgs/{slug} | operator bearer | shape not published internal |
| GET | /api/admin/orgs/{slug}/admins | GET /api/admin/orgs/{slug}/admins | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/admins | POST /api/admin/orgs/{slug}/admins | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/admins/remove | POST /api/admin/orgs/{slug}/admins/remove | operator bearer | shape not published internal |
| GET | /api/admin/orgs/{slug}/domain | GET /api/admin/orgs/{slug}/domain | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/domain | POST /api/admin/orgs/{slug}/domain | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/domain/activate | POST /api/admin/orgs/{slug}/domain/activate | operator bearer | shape not published internal |
| GET | /api/admin/orgs/{slug}/domain/document | GET /api/admin/orgs/{slug}/domain/document | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/domain/revoke | POST /api/admin/orgs/{slug}/domain/revoke | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/domain/verify | POST /api/admin/orgs/{slug}/domain/verify | operator bearer | shape not published internal |
| GET | /api/admin/orgs/{slug}/lifecycle | GET /api/admin/orgs/{slug}/lifecycle | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/reactivate | POST /api/admin/orgs/{slug}/reactivate | operator bearer | shape not published internal |
| POST | /api/admin/orgs/{slug}/suspend | POST /api/admin/orgs/{slug}/suspend | operator bearer | shape not published internal |
| POST | /api/admin/orgs/preview-branding | POST /api/admin/orgs/preview-branding | operator bearer | shape not published internal |
| GET | /api/admin/partners | GET /api/admin/partners | operator bearer | shape not published internal |
| POST | /api/admin/partners | POST /api/admin/partners | operator bearer | shape not published internal |
| GET | /api/admin/partners/{id} | GET /api/admin/partners/{id} | operator bearer | shape not published internal |
| POST | /api/admin/partners/{id}/link-tenant | POST /api/admin/partners/{id}/link-tenant | operator bearer | shape not published internal |
| POST | /api/admin/partners/{id}/stage | POST /api/admin/partners/{id}/stage | operator bearer | shape not published internal |
| GET | /api/admin/partners/portfolio | GET /api/admin/partners/portfolio | operator bearer | shape not published internal |
| GET | /api/admin/people/coin-ledger | GET /api/admin/people/coin-ledger | operator bearer | shape not published internal |
| GET | /api/admin/people/consent | GET /api/admin/people/consent | operator bearer | shape not published internal |
| GET | /api/admin/people/economy | GET /api/admin/people/economy | operator bearer | shape not published internal |
| GET | /api/admin/people/governance | GET /api/admin/people/governance | operator bearer | shape not published internal |
| POST | /api/admin/people/governance | POST /api/admin/people/governance | operator bearer | shape not published internal |
| GET | /api/admin/people/members | GET /api/admin/people/members | operator bearer | shape not published internal |
| GET | /api/admin/people/operators | GET /api/admin/people/operators | operator bearer | shape not published internal |
| POST | /api/admin/people/operators | POST /api/admin/people/operators | operator bearer | shape not published internal |
| GET | /api/admin/people/operators/{username} | GET /api/admin/people/operators/{username} | operator bearer | shape not published internal |
| POST | /api/admin/people/operators/{username}/enable | POST /api/admin/people/operators/{username}/enable | operator bearer | shape not published internal |
| POST | /api/admin/people/operators/{username}/entra | POST /api/admin/people/operators/{username}/entra | operator bearer | shape not published internal |
| POST | /api/admin/people/operators/{username}/password | POST /api/admin/people/operators/{username}/password | operator bearer | shape not published internal |
| POST | /api/admin/people/operators/{username}/remove | POST /api/admin/people/operators/{username}/remove | operator bearer | shape not published internal |
| POST | /api/admin/people/operators/{username}/roles | POST /api/admin/people/operators/{username}/roles | operator bearer | shape not published internal |
| POST | /api/admin/people/operators/{username}/suspend | POST /api/admin/people/operators/{username}/suspend | operator bearer | shape not published internal |
| GET | /api/admin/search | GET /api/admin/search | operator bearer | shape not published internal |
| GET | /api/admin/support/credential/{statusIndex} | GET /api/admin/support/credential/{statusIndex} | operator bearer | shape not published internal |
| GET | /api/admin/support/lookup | GET /api/admin/support/lookup | operator bearer | shape not published internal |
| GET | /api/admin/support/timeline/{subject} | GET /api/admin/support/timeline/{subject} | operator bearer | shape not published internal |
| GET | /api/admin/today | GET /api/admin/today | operator bearer | shape not published internal |
| GET | /api/admin/types | GET /api/admin/types | operator bearer | shape not published internal |
| POST | /api/admin/types | POST /api/admin/types | operator bearer | shape not published internal |
Operator console 19
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/agent/activity | Report an agent’s own tool-call activity | operator bearer | shape not published internal |
| GET | /api/compliance | Compliance summary | operator bearer | shape not published internal |
| POST | /api/operators | Create an operator account | operator bearer | shape not published internal |
| POST | /api/operators/enable | Reinstate a suspended operator account | operator bearer | shape not published internal |
| POST | /api/operators/remove | Remove an operator account | operator bearer | shape not published internal |
| POST | /api/operators/roles | Grant xFIN / platform financial roles | operator bearer | shape not published internal |
| POST | /api/operators/suspend | Suspend an operator account | operator bearer | shape not published internal |
| GET | /api/org-channels | Organization comms channels (read) | operator bearer | shape not published internal |
| POST | /api/org-channels/{tenant}/{accountId}/accept | Apply an accept decision to an org channel | operator bearer | shape not published internal |
| POST | /api/org-channels/{tenant}/{accountId}/decline | Apply a decline decision to an org channel | operator bearer | shape not published internal |
| POST | /api/org-channels/{tenant}/{accountId}/revoke | Apply a revoke decision to an org channel | operator bearer | shape not published internal |
| POST | /api/org-channels/request | Request an organization comms channel | operator bearer | shape not published internal |
| GET | /api/qr | Render a string as an SVG QR code | public | — |
| GET | /api/state | Console state snapshot | operator bearer | shape not published internal |
| POST | /api/trust/remove | Remove an issuer from the trust registry | operator bearer | shape not published internal |
| POST | /api/trust/restore | Restore an issuer to the trust registry | operator bearer | shape not published internal |
| POST | /api/wallets | Create a console-side test wallet | operator bearer | shape not published internal |
| POST | /api/wallets/accept | Console test wallet accepts an offer | operator bearer | shape not published internal |
| POST | /api/wallets/respond | Console test wallet answers a presentation request | operator bearer | shape not published internal |
Operator sign-in 6
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/login | Exchange operator credentials for a session bearer | public | — |
| GET | /auth/api-token | Exchange an Entra sign-in for an operator API session | Entra session | shape not published |
| GET | /auth/callback | Microsoft Entra redirect target | public | config-dependent |
| GET | /auth/login | Begin Microsoft Entra sign-in | public | config-dependent |
| GET | /auth/logout | End the Entra session | Entra session | config-dependent |
| GET | /auth/whoami | Show the caller’s own Entra object id and link status | Entra session | shape not published config-dependent |
Membership & governance 8
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/members/enroll | Enroll a member (operator plane) | operator bearer | shape not published internal |
| POST | /api/members/update | Update a member record | operator bearer | shape not published internal |
| POST | /api/proposals | Create a governance proposal | operator bearer | shape not published internal |
| POST | /api/proposals/vote | Cast a vote (operator plane) | operator bearer | shape not published internal |
| POST | /member/enroll | Enroll as a member using a verified presentation | public | — |
| GET | /member/me | The holder’s own memberships and open proposals | holder JWS | — |
| POST | /member/vote | Cast a governance vote | holder JWS | shape not published |
| GET | /v1/member/me | The holder’s own memberships and open proposals | holder JWS | — |
Partner onboarding (public) 21
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/partners/applications | Apply to become an ORBIS.ID partner | public | — |
| POST | /api/partners/applications/status | Read your own partner application | public | — |
| GET | /api/partners/keys | List this partner’s own keys | operator bearer | shape not published |
| POST | /api/partners/keys | Mint a key inside the grant | operator bearer | shape not published |
| POST | /api/partners/keys/{id}/revoke | Revoke one of this partner’s keys | operator bearer | shape not published |
| POST | /api/partners/keys/{id}/rotate | Rotate one of this partner’s keys | operator bearer | shape not published |
| POST | /api/partners/session/start | Begin a partner key-management session | public | shape not published |
| POST | /api/partners/session/verify | Exchange the code for a session | public | shape not published |
| GET | /portal | The partner portal — where you are in the pipeline | operator bearer | — |
| GET | /portal/keys | The partner portal — your API keys | operator bearer | — |
| POST | /portal/keys/mint | Issue a key from the portal | operator bearer | — |
| POST | /portal/keys/revoke | Revoke a key from the portal | operator bearer | — |
| POST | /portal/keys/rotate | Rotate a key from the portal | operator bearer | — |
| GET | /portal/signin | The partner portal sign-in page | public | — |
| POST | /portal/signin | Ask the portal for a sign-in code | public | — |
| POST | /portal/signout | End the browser session | operator bearer | — |
| GET | /portal/team | The partner portal — who is on this account | operator bearer | — |
| POST | /portal/team/add | Add a contact to the partner account | operator bearer | — |
| POST | /portal/team/remove | Remove a contact from the partner account | operator bearer | — |
| POST | /portal/team/role | Change a contact’s role | operator bearer | — |
| POST | /portal/verify | Exchange the code for a browser session | public | — |
Wallet bridge (holder) 10
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/demo/seed | Seed the demo-bridge catalogue | operator bearer | shape not published internal |
| GET | /api/products | Demo-bridge product catalogue | operator bearer | shape not published internal |
| POST | /api/products | Create a demo-bridge product | operator bearer | shape not published internal |
| POST | /api/products/{productId}/approve | Approve a demo-bridge product | operator bearer | shape not published internal |
| POST | /api/products/{productId}/require-channel | Require an org channel for a demo-bridge product | operator bearer | shape not published internal |
| GET | /api/wallet/{accountId}/items | The wallet items held by an account | public | shape not published |
| POST | /api/wallet/{accountId}/items/{itemId}/accept | Accept an offered item | holder JWS | shape not published |
| POST | /api/wallet/{accountId}/items/{itemId}/sharing | Set per-element sharing on a held item | holder JWS | shape not published |
| POST | /v1/wallet/{accountId}/items/{itemId}/accept | Accept an offered item | holder JWS | shape not published |
| POST | /v1/wallet/{accountId}/items/{itemId}/sharing | Set per-element sharing on a held item | holder JWS | shape not published |
Presentation — OID4VP 3
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/requests | Create a presentation request (operator plane) | operator bearer | shape not published |
| GET | /oid4vp/request/{transactionId} | Authorization request object (DCQL query) | public | — |
| POST | /oid4vp/response | Wallet posts the presentation (direct_post) | public | shape not published |
Tenants 3
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/tenants | Provision a tenant issuer | operator bearer | — |
| POST | /api/tenants/update | Update an organization profile and branding | operator bearer | — |
| POST | /v1/tenants | Provision a tenant issuer | operator bearer | — |
Deposit rail 3
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/tenants/{tenant}/bills | Deposit a business event as a wallet item (contracts 0.3.0) | operator bearer | — |
| POST | /api/tenants/{tenant}/revoke | Revoke a credential issued by this organization | operator bearer | — |
| POST | /v1/tenants/{tenant}/bills | Deposit a business event as a wallet item (contracts 0.3.0) | operator bearer | — |
Credential types 2
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /api/types | Register a credential type | operator bearer | shape not published |
| POST | /v1/types | Register a credential type | operator bearer | shape not published |
Holder directory 7
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /directory/descriptor | Fetch a directory descriptor | holder JWS | shape not published |
| DELETE | /directory/listing | Remove the holder’s own listing | holder JWS | shape not published |
| POST | /directory/listing | Create or update the holder’s own directory listing | holder JWS | shape not published |
| GET | /directory/listing/me | Read the holder’s own listing | holder JWS | shape not published |
| POST | /directory/lookup | Look up a hashed directory entry | holder JWS | shape not published |
| POST | /directory/search/community | Search the directory by community | holder JWS | shape not published |
| POST | /directory/search/name | Search the directory by name | holder JWS | shape not published |
Holder onboarding 2
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| POST | /onboard/start | Send an identifier-ownership code | public | shape not published |
| POST | /onboard/verify | Prove the code and receive a signed ownership attestation | public | — |
Hosted verification 3
| Method | Path | Operation | Auth | Notes |
|---|---|---|---|---|
| GET | /verify/catalog | Credential types that can be requested | public | — |
| POST | /verify/request | Create a presentation request | public | — |
| GET | /verify/result/{transactionId} | Poll the outcome of a presentation | public | — |