OktoID platform API (v1)

Download OpenAPI specification:

The public gateway of OktoID, a national-scale self-sovereign digital identity platform. Its routes accept the staff population (x-oktoid-population). Generated from the contracts by apps/gateway/scripts/openapi.ts; never edited by hand. Health routes are internal and are not part of this API.

subjects

Create an organization below the caller scope

Requires Idempotency-Key. kind may be omitted from the body; the gateway then reads it as organization, the only kind slice 0 creates. The new subject path is parentPath (default: the caller scope path) plus slug; a parent outside the caller scope is refused.

Two rules on displayName beyond its schema, both because an organization name is published in the trust tree once the organization is admitted. Its letters must come from the scripts this deployment allows (Latin and Arabic unless the operator configured otherwise), and no unbroken run of letters may mix two scripts (a space, hyphen, slash, digit or any punctuation ends a run, so a Latin word joined to an Arabic word by a hyphen is accepted); digits, punctuation and combining marks are always allowed. A name that fails is refused with 400 and code subject.name_script. And it must not read the same as the name of an organization above the new one or beside it, once capitals, character width, invisible characters and spacing are set aside: 409 with code subject.name_taken, whose detail names the path of that organization. Only organizations inside the caller scope are compared, so a 201 is not a promise: the same comparison is made across the whole trust tree when the organization is admitted.

Authorizations:
staffBearer
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Request Body schema: application/json
required
kind
required
string
Value: "organization"
slug
required
string^(?!..--)
displayName
required
string [ 1 .. 200 ] characters ^(?![\u061c\u200e\u200f])(?=.*[^\p{White_Spac...
parentPath
string <= 1024 characters ^(/[a-z0-9][a-z0-9-]{0,62}){1,16}$

Responses

Request samples

Content type
application/json
{
  • "kind": "organization",
  • "slug": "acme",
  • "displayName": "Acme Licensing Office",
  • "parentPath": "/authority"
}

Response samples

Content type
application/json
{
  • "id": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "kind": "organization",
  • "displayName": "Acme Licensing Office",
  • "path": "/authority/acme",
  • "status": "active",
  • "createdAt": "2026-03-03T09:41:07.552Z"
}

Read one subject

A subject that does not exist and one outside the caller scope get the same 404.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
{
  • "id": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "kind": "organization",
  • "displayName": "Acme Licensing Office",
  • "path": "/authority/acme",
  • "status": "active",
  • "createdAt": "2026-03-03T09:41:07.552Z"
}

persons

Create a person below the caller scope

Requires Idempotency-Key. The person is a leaf below parentPath (default: the caller scope path); a parent outside the caller scope, or a parent that is itself a person, is refused. givenName, familyName and email are personal data: stored encrypted under the person's own key, never logged, and absent from the reply once the person is erased. An address another person already has (compared without regard to case) is refused with 409 and code person.email_taken. invitation says where the invitation stands.

Authorizations:
staffBearer
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Request Body schema: application/json
required
givenName
required
string [ 1 .. 200 ] characters ^(?![\u061c\u200e\u200f])(?=.*[^\p{White_Spac...
familyName
required
string [ 1 .. 200 ] characters ^(?![\u061c\u200e\u200f])(?=.*[^\p{White_Spac...
email
required
string <email> [ 3 .. 254 ] characters
parentPath
string <= 1024 characters ^(/[a-z0-9][a-z0-9-]{0,62}){1,16}$
invite
boolean

Responses

Request samples

Content type
application/json
{
  • "givenName": "Layla",
  • "familyName": "Haddad",
  • "email": "layla.haddad@acme.example.org",
  • "parentPath": "/authority/acme",
  • "invite": true
}

Response samples

Content type
application/json
{
  • "id": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "kind": "person",
  • "path": "/authority/acme/0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "status": "active",
  • "createdAt": "2026-03-03T09:44:30.118Z",
  • "erased": false,
  • "givenName": "Layla",
  • "familyName": "Haddad",
  • "email": "layla.haddad@acme.example.org",
  • "invitation": {
    }
}

Read one person

A person that does not exist and one outside the caller scope get the same 404. After erasure the reply has erased: true and no personal field.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
Example
{
  • "id": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "kind": "person",
  • "path": "/authority/acme/0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "status": "active",
  • "createdAt": "2026-03-03T09:44:30.118Z",
  • "erased": false,
  • "givenName": "Layla",
  • "familyName": "Haddad",
  • "email": "layla.haddad@acme.example.org",
  • "invitation": {
    }
}

Invite a person to the staff realm again

Requires Idempotency-Key; takes no body. Sends a fresh invitation to the address on file, for an invitation that expired, failed or was never asked for. An invitation still in progress is answered as it stands and nothing new is sent. Takes the person.create permission. An erased person, and one with no address on file (the root account), are refused with 409 state.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
Example
{
  • "personId": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "invitation": {
    }
}

grants

Grant a bundle to a grantee for an organization

Requires Idempotency-Key. Delegation narrows: the bundle's permissions must be a subset of the caller's own, for an organization inside the caller's own subtree; anything else is refused in the standard shape. Revoking a grant takes effect within one token lifetime: the gateway remembers a resolved principal for at most five minutes (principalCacheSeconds, never past the token's own expiry), and a revoked grant authorises until that entry expires.

Authorizations:
staffBearer
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Request Body schema: application/json
required
granteeId
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
organizationId
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
bundle
required
string^[A-Z][A-Z0-9_]{0,63}$

Responses

Request samples

Content type
application/json
{
  • "granteeId": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "bundle": "ENTITY_ADMIN"
}

Response samples

Content type
application/json
{
  • "id": "0199c1f4-7c20-7e5b-8d16-93a0b4c5e2f1",
  • "granteeId": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "bundle": "ENTITY_ADMIN",
  • "grantedBy": "0199c1d0-5b7e-7a21-9c43-2f8e1d6a0b57",
  • "status": "active",
  • "createdAt": "2026-03-03T09:46:02.871Z"
}

List the grants inside the caller scope

Filtered by organization, grantee and status, newest first, in pages: pass nextCursor back as cursor. Revoking a grant takes effect within one token lifetime: the gateway remembers a resolved principal for at most five minutes (principalCacheSeconds, never past the token's own expiry), and a revoked grant authorises until that entry expires.

Authorizations:
staffBearer
query Parameters
organization
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

Only grants for this organization.

grantee
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

Only grants held by this grantee.

status
any
Enum: "active" "revoked"
limit
integer [ 1 .. 200 ]
cursor
string [ 1 .. 256 ] characters
header Parameters
Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
Example
{
  • "grants": [
    ],
  • "nextCursor": "page-2.0199c1f4-7c20"
}

Revoke a grant

Requires Idempotency-Key; takes no body. Revoking an already revoked grant answers the same revoked grant. Revoking a grant takes effect within one token lifetime: the gateway remembers a resolved principal for at most five minutes (principalCacheSeconds, never past the token's own expiry), and a revoked grant authorises until that entry expires.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
{
  • "id": "0199c1f4-7c20-7e5b-8d16-93a0b4c5e2f1",
  • "granteeId": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "bundle": "ENTITY_ADMIN",
  • "grantedBy": "0199c1d0-5b7e-7a21-9c43-2f8e1d6a0b57",
  • "status": "revoked",
  • "createdAt": "2026-03-03T09:46:02.871Z",
  • "revokedAt": "2026-04-14T15:20:44.309Z"
}

me

The organizations the caller holds a grant at

Answers the caller's own active grants: for each, the organization id (the value to send as Oktoid-Acting-For on every other call), its path and the bundle code. It acts for no organization, so it takes no Oktoid-Acting-For (a header sent is ignored) and needs no permission. A caller with no grant, a token that maps to no principal and a suspended principal all get 200 with an empty list. Read fresh on every call, not through the principal cache, so a grant made or revoked shows at once.

Authorizations:
staffBearer

Responses

Response samples

Content type
application/json
Example
{
  • "grants": [
    ]
}

identifiers

Create a decentralised identifier for an organization

Requires Idempotency-Key. Names an organization inside the caller scope; the issuance module reads the organization itself and takes nothing else about it from the body. An organization may hold one identifier that has not failed: a second is refused with 409 and code identifier.exists.

Two methods exist. webvh, the default, publishes a signed, append-only log: every change is an entry chained to the one before, so a verifier can tell a key rotation from a substitution, and the next update key is committed in advance, so a lost key does not freeze the identifier. web publishes one replaceable document with no history and no recovery, and whoever controls the domain can replace it; choose it only for verifiers that resolve nothing else. A webvh identifier is also served in did:web form beside its log, so it rarely needs choosing. The method is fixed at creation, and a deployment may not permit web, in which case asking for it is refused with 400 and code identifier.method_not_permitted, at body.method.

The answer is 202 and the identifier in state placing: its keys are generated and its document rendered just after this reply, once the organization has been placed on an agent. Read the identifier at the Location given until it is prepared, then preview it and publish it. Nothing is published by creating.

host serves the identifier on a delegated host instead of the organization's own publisher host. Its DNS must already point at the platform: that is checked at every publication, which is refused with 409 and code identifier.host_unverified while the name resolves elsewhere; correct the DNS record and publish again. A name that is an address, or that sits under the platform's own domains anywhere but beneath the organization's own, is refused with 400 and code identifier.host_refused, at body.host. Without host, the organization's own publisher host is used, and when another identifier already serves that host the answer is 409 with code identifier.host_taken; the answer never says whose.

Authorizations:
staffBearer
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Request Body schema: application/json
required
organizationId
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization to create the identifier for: an active organization inside the scope the caller acts for. It may hold one identifier that has not failed.

"webvh" (string) or "web" (string)
Default: "webvh"

The DID method. Leave it out for webvh, the default: a signed, append-only log that lets a verifier tell a key rotation from a substitution and survives a lost key. Choose web only for verifiers that resolve nothing else: it is one replaceable document with no history and no recovery, and whoever controls the domain can replace it. The choice is permanent for this identifier, and a deployment may not permit web at all.

host
string <= 253 characters ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-...

A delegated host to serve the identifier on, such as id.acme.example, whose DNS already points at the platform. Leave it out to use the organization's own publisher host, <slug>.identity.<domain>. A name under the platform's own domains is refused.

Responses

Request samples

Content type
application/json
Example
{
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e"
}

Response samples

Content type
application/json
{
  • "id": "0199c2a0-4e1b-7d3c-8f25-6a9b0c7d1e42",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "path": "/authority/acme",
  • "method": "webvh",
  • "history": true,
  • "host": "acme.identity.example.org",
  • "status": "placing",
  • "version": 0,
  • "versionLimit": 24,
  • "createdAt": "2026-03-03T10:02:11.004Z",
  • "createdBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914"
}

List the identifiers inside the caller scope

Filtered by organization and status, in pages: pass nextCursor back as cursor. A cursor this list did not issue is refused with 400 and code identifier.cursor_invalid, at query.cursor. An identifier outside the caller scope is never listed.

Authorizations:
staffBearer
query Parameters
organization
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

Only the identifier of this organization.

status
any
Enum: "placing" "prepared" "published" "failed"

Only identifiers in this state.

limit
integer [ 1 .. 200 ]

The most entries to return in one page, at most 200.

cursor
string [ 1 .. 256 ] characters

The nextCursor of the previous page. A value this list did not issue is refused with 400.

header Parameters
Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
{
  • "identifiers": [
    ]
}

Read one identifier

An identifier that does not exist and one outside the caller scope get the same 404. This is the route to poll after creating: status moves from placing to prepared within seconds. method and history say which kind of identifier it is: history is false for did:web, whose changes cannot be verified against what came before.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
Example
{
  • "id": "0199c2a0-4e1b-7d3c-8f25-6a9b0c7d1e42",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "path": "/authority/acme",
  • "method": "webvh",
  • "history": true,
  • "host": "acme.identity.example.org",
  • "status": "prepared",
  • "version": 0,
  • "versionLimit": 24,
  • "createdAt": "2026-03-03T10:02:11.004Z",
  • "createdBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "assertionKeyId": "dMm7JVVgAw3vNZgfqLqxnoGoLyOQgDPotMP76t_2UHw",
  • "updateKeyId": "uAztFfC0taJ4RV-0lO8cfLZ60yrp4RsBuDMsmyZC6kI",
  • "nextUpdateKeyId": "Qk3v9mT1xW7cHn2ZpL5sYd8RbA0uEo6FgJ4iKqVtN_s",
  • "renderedAt": "2026-03-03T10:02:12.870Z",
  • "previewedAt": "2026-03-03T10:05:40.215Z",
  • "previewedBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "previewedDigest": "96f7627af971a755b1f91af028f14d87cd5679ec5ae56e847582b2249460b505"
}

Read the exact DID document that will be published

The document as it will be published, the URLs it will be served at and the keys behind it. Reading it is what allows publication: publish is refused until the current document has been previewed, and a document rendered again since the last preview needs a new one. document is canonical JSON text; parse it to read it, and compare documentDigest with the SHA-256 of its bytes to know nothing changed it on the way. For a webvh identifier, parallelDocument is the did:web form served beside the log. An identifier still in placing has no document yet and is refused with 409; read the identifier until it is prepared.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
{
  • "identifier": {
    },
  • "version": 1,
  • "document": "{\"@context\":[\"https://www.w3.org/ns/did/v1\",\"https://w3id.org/security/multikey/v1\"],\"alsoKnownAs\":[\"https://acme.identity.example.org\",\"did:web:acme.identity.example.org\"],\"assertionMethod\":[\"did:webvh:{SCID}:acme.identity.example.org#key-1\"],\"authentication\":[\"did:webvh:{SCID}:acme.identity.example.org#key-1\"],\"id\":\"did:webvh:{SCID}:acme.identity.example.org\",\"verificationMethod\":[{\"controller\":\"did:webvh:{SCID}:acme.identity.example.org\",\"id\":\"did:webvh:{SCID}:acme.identity.example.org#key-1\",\"publicKeyMultibase\":\"zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169\",\"type\":\"Multikey\"}]}",
  • "documentDigest": "96f7627af971a755b1f91af028f14d87cd5679ec5ae56e847582b2249460b505",
  • "parallelDocument": "{\"@context\":[\"https://www.w3.org/ns/did/v1\",\"https://w3id.org/security/multikey/v1\"],\"alsoKnownAs\":[\"https://acme.identity.example.org\",\"did:webvh:{SCID}:acme.identity.example.org\"],\"assertionMethod\":[\"did:web:acme.identity.example.org#key-1\"],\"authentication\":[\"did:web:acme.identity.example.org#key-1\"],\"id\":\"did:web:acme.identity.example.org\",\"verificationMethod\":[{\"controller\":\"did:web:acme.identity.example.org\",\"id\":\"did:web:acme.identity.example.org#key-1\",\"publicKeyMultibase\":\"zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169\",\"type\":\"Multikey\"}]}",
  • "scid": null,
  • "scidPlaceholder": "{SCID}"
}

Publish the previewed DID document

Requires Idempotency-Key; takes no body. The agent signs the previewed document with keys that never leave their token, and the result is written to the identifier host: the log at /.well-known/did.jsonl and the did:web form at /.well-known/did.json for webvh, the second alone for web. The identifier then resolves, and a resolver may go on seeing the previous version for up to five minutes.

Refused with 409 when the current document has not been previewed, when the identifier is not prepared, and when another publication of the same identifier won the race; read the identifier again and decide. What is signed is compared with what was previewed before a byte is written. If they differ nothing is published, the identifier becomes failed with failureReason: document-mismatch and its keys are destroyed, and the refusal is recorded in the audit trail.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
{
  • "id": "0199c2a0-4e1b-7d3c-8f25-6a9b0c7d1e42",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "path": "/authority/acme",
  • "method": "webvh",
  • "history": true,
  • "host": "acme.identity.example.org",
  • "status": "published",
  • "version": 1,
  • "versionLimit": 24,
  • "createdAt": "2026-03-03T10:02:11.004Z",
  • "createdBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "assertionKeyId": "dMm7JVVgAw3vNZgfqLqxnoGoLyOQgDPotMP76t_2UHw",
  • "updateKeyId": "Qk3v9mT1xW7cHn2ZpL5sYd8RbA0uEo6FgJ4iKqVtN_s",
  • "renderedAt": "2026-03-03T10:02:12.870Z",
  • "previewedAt": "2026-03-03T10:05:40.215Z",
  • "previewedBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "previewedDigest": "96f7627af971a755b1f91af028f14d87cd5679ec5ae56e847582b2249460b505",
  • "did": "did:webvh:QmfGEUAcMpzo25kF2Rhn8L5FAXysfGnkzjwdKoNPi615XQ:acme.identity.example.org",
  • "scid": "QmfGEUAcMpzo25kF2Rhn8L5FAXysfGnkzjwdKoNPi615XQ",
  • "versionId": "1-QmQq6Kg4ZZ1p49znzxnWmes4LkkWgMWLrnrfPre8UD56bz",
  • "retiredUpdateKeyIds": [
    ],
  • "publishedAt": "2026-03-03T10:06:02.593Z"
}

Begin a key rotation for a published identifier

Requires Idempotency-Key; takes no body. Generates a new assertion key and renders the document again with it. The identifier returns to state prepared and the version already published stays served; preview the new document, then publish it. For webvh the publication appends one log entry, signed by the current update key, that a verifier can check against the entry before. For web it overwrites the document, and nothing lets a verifier tell that from a substitution. The replaced key is kept and listed in retiredAssertionKeyIds, because a credential signed under it must stay verifiable. The host and the method cannot be changed. An identifier that is not published is refused with 409.

Authorizations:
staffBearer
path Parameters
id
required
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...
header Parameters
Idempotency-Key
required
string^[\x21-\x7e]{8,128}$

8 to 128 printable ASCII characters, unique per intended change and reused on a retry of it.

Oktoid-Acting-For
string^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]...

The organization (a subject id) the caller acts for. A human caller sends it; a caller with no grant for that organization is refused in the same shape as one with no grant at all, which does not say whether the organization exists. A machine client sends none.

Responses

Response samples

Content type
application/json
{
  • "id": "0199c2a0-4e1b-7d3c-8f25-6a9b0c7d1e42",
  • "organizationId": "0199c1f1-03aa-7d10-b6c2-51e4a7f09d3e",
  • "path": "/authority/acme",
  • "method": "webvh",
  • "history": true,
  • "host": "acme.identity.example.org",
  • "status": "prepared",
  • "version": 0,
  • "versionLimit": 24,
  • "createdAt": "2026-03-03T10:02:11.004Z",
  • "createdBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "assertionKeyId": "dMm7JVVgAw3vNZgfqLqxnoGoLyOQgDPotMP76t_2UHw",
  • "updateKeyId": "uAztFfC0taJ4RV-0lO8cfLZ60yrp4RsBuDMsmyZC6kI",
  • "nextUpdateKeyId": "Qk3v9mT1xW7cHn2ZpL5sYd8RbA0uEo6FgJ4iKqVtN_s",
  • "renderedAt": "2026-03-03T10:02:12.870Z",
  • "previewedAt": "2026-03-03T10:05:40.215Z",
  • "previewedBy": "0199c1ee-9f40-73b8-a1d7-6c2b0e58f914",
  • "previewedDigest": "96f7627af971a755b1f91af028f14d87cd5679ec5ae56e847582b2249460b505"
}

meta

The service and the release it was built from

Unauthenticated, and answered by both surfaces. The deploy agent waits on this until the release it deployed is the one reported. version is what the image build set: 0.0.0+ and the abbreviated commit the image was built from, or dev for a build that set none.

Responses

Response samples

Content type
application/json
{
  • "service": "gateway",
  • "version": "0.0.0+7e3ad643a1f2"
}