Docs API reference
Organizations
Organizations, members, invitations and service tokens.
18 operations. See the API overview for authentication, errors, idempotency and caching rules that apply to all of them.
List my organizations
GET/v1/organizations
A person gets every organization they are an active member of (oldest first), each with the caller's role. A service
token gets the single organization it is bound to, with its scopes in my_roles.
Responses
| Status | Description | Body |
|---|---|---|
| 200 | Organizations. | application/json OrganizationList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"organizations": [
{
"id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"legal_name": "Example Payments, Inc.",
"jurisdiction": "US-DE",
"registration_reference": "DE-5849201",
"verification_status": "verified",
"version": 4,
"created_at": "2026-10-05T11:40:00.000Z",
"updated_at": "2026-10-05T12:30:00.000Z",
"my_roles": [
"organization_owner"
]
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Create an organization
POST/v1/organizations
Creates a draft organization and makes the caller its owner. Human only; platform staff accounts cannot create or
belong to organizations. At most 5 organizations per principal per rolling 24 hours (429). Identity is verified later by
review, through a claim.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Request body
application/json, required: CreateOrganizationRequest
| Field | Type | Required | Description |
|---|---|---|---|
legal_name | string | Yes | NFC-normalized, trimmed, 1 to 200 code points, no control or bidi characters. 1 to 200 characters |
jurisdiction | string | Yes | ISO 3166-1 alpha-2 country, optionally with an ISO 3166-2 subdivision (for example US or US-DE)., examples: [US-DE] |
registration_reference | string or null | No | at most 100 characters |
Example
{
"legal_name": "Example Payments, Inc.",
"jurisdiction": "US-DE",
"registration_reference": "DE-5849201"
}
Responses
| Status | Description | Body |
|---|---|---|
| 201 | The new organization. | application/json Organization |
| 400 | invalid_request. Malformed JSON, an invalid or unknown field (see field_errors), a missing or malformed
Idempotency-Key, or a bad query. Also used with status 413 (body over 64 KiB) and 415 (wrong Content-Type). | application/json Error |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 429 | rate_limited. A per-client or per-record limit was reached; retry later. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
201 example
{
"id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"legal_name": "Example Payments, Inc.",
"jurisdiction": "US-DE",
"registration_reference": "DE-5849201",
"verification_status": "draft",
"version": 1,
"created_at": "2026-10-05T11:40:00.000Z",
"updated_at": "2026-10-05T11:40:00.000Z",
"my_roles": [
"organization_owner"
]
}
400 example
{
"code": "invalid_request",
"message": "Invalid organization.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "legal_name",
"message": "must be 1-200 characters"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
429 example
{
"code": "rate_limited",
"message": "Too many requests.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Get an organization
GET/v1/organizations/{id}
Any member (or a service token bound to this organization).
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The organization. | application/json Organization |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"legal_name": "Example Payments, Inc.",
"jurisdiction": "US-DE",
"registration_reference": "DE-5849201",
"verification_status": "verified",
"version": 4,
"created_at": "2026-10-05T11:40:00.000Z",
"updated_at": "2026-10-05T12:30:00.000Z",
"my_roles": [
"organization_owner"
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Edit an organization's legal identity
PATCH/v1/organizations/{id}
Owner only, human only. expected_version is optimistic concurrency: a stale value is 409. At least one field to change
is required. Send registration_reference: null (or "") to clear it.
A draft organization is edited freely. Once its identity has been reviewed (verified or pending_review), an edit is a
material change (spec §4): it needs recent step-up, at least one value must actually change (400 otherwise), and in the
same commit the organization returns to pending_review and every claim that passed identity review goes back to
pending_identity. Their issuer bindings are suspended (resumable for 30 days once a reviewer re-approves identity and
authority); delegations granted under them are revoked. A public claim.state_changed event is written for each claim that
leaves verified.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Request body
application/json, required: UpdateOrganizationRequest
| Field | Type | Required | Description |
|---|---|---|---|
expected_version | integer | Yes | minimum 1 |
legal_name | string | No | 1 to 200 characters |
jurisdiction | string | No | |
registration_reference | string or null | No | null or "" clears it.at most 100 characters |
Example
{
"expected_version": 1,
"legal_name": "Example Payments, Incorporated"
}
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The updated organization. | application/json Organization |
| 400 | invalid_request. Malformed JSON, an invalid or unknown field (see field_errors), a missing or malformed
Idempotency-Key, or a bad query. Also used with status 413 (body over 64 KiB) and 415 (wrong Content-Type). | application/json Error |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 422 | semantic_check_failed. The request is well formed but a policy or proof check failed (for example an issuer-control proof
did not verify, a challenge expired, or the organization is not yet verified). field_errors[].message is often a stable
reason code. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"legal_name": "Example Payments, Incorporated",
"jurisdiction": "US-DE",
"registration_reference": "DE-5849201",
"verification_status": "draft",
"version": 2,
"created_at": "2026-10-05T11:40:00.000Z",
"updated_at": "2026-10-05T11:45:00.000Z",
"my_roles": [
"organization_owner"
]
}
400 example
{
"code": "invalid_request",
"message": "Invalid organization.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "legal_name",
"message": "must be 1-200 characters"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
422 example
{
"code": "semantic_check_failed",
"message": "Issuer-control proof failed (endpoint_http_404). 4 attempt(s) left on this challenge.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "method",
"message": "endpoint_http_404"
}
]
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
List the organization's claims
GET/v1/organizations/{id}/claims
Any member. Newest first, at most 200. Use GET /v1/claims/{id} for detail.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | Claims. | application/json ClaimList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"claims": [
{
"id": "clm_4c8e2g6k0m3q7s1w5y9a2d6f8h",
"domain": "payments.example.com",
"state": "verified",
"resume_state": null,
"version": 7,
"created_at": "2026-10-05T11:50:00.000Z",
"updated_at": "2026-10-05T12:20:00.000Z",
"authority_expires_at": "2027-10-05T12:20:00.000Z",
"permitted_actions": [
"authorize_issuer",
"delegate"
]
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
List the organization's issuer bindings
GET/v1/organizations/{id}/issuer-bindings
Any member. Newest first, at most 200.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | Issuer bindings (summaries). | application/json IssuerBindingList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"issuer_bindings": [
{
"id": "bnd_9r2t5w8z1b4d7f0h3k6n9q2s5v",
"trust_domain": "agents.payments.example.com",
"verified_domain": "payments.example.com",
"mapping": "dns",
"profile": "spiffe-x509",
"workload_scope": [
"/agent/checkout"
],
"environment": "production",
"status": "active",
"version": 6,
"current_version": 1,
"expires_at": "2027-10-05T11:58:12.000Z",
"created_at": "2026-10-05T11:56:00.000Z",
"updated_at": "2026-10-05T11:58:12.000Z",
"latest_version": 1
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
List active members
GET/v1/organizations/{id}/members
Any member. Email addresses are shown only to owners.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | Active members. | application/json MemberList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"members": [
{
"principal_id": "prn_6d9g2j5m8q1t4w7z0c3f6h9k2n",
"role": "organization_owner",
"status": "active",
"version": 1,
"display_name": "Ada Lovelace",
"idp": "github",
"email": "ada@example.com",
"created_at": "2026-10-05T11:40:00.000Z",
"updated_at": "2026-10-05T11:40:00.000Z"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Change a member's role
PATCH/v1/organizations/{id}/members/{principalId}
Owner, human, step-up. expected_version is the member's version. Demoting an owner revokes the service tokens they
created for this organization in the same commit. The last owner can never be demoted (409).
Material change (spec §4): if the member is the approved representative of a claim and the new role is neither the role
they held when their authority was approved nor organization_owner, that claim goes back to pending_authority in the
same commit, its issuer bindings are suspended (resumable for 30 days after re-approval) and a public claim.state_changed
event is written.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
principalId | path | Yes | string | A member's principal id. DELETE also accepts the literal me.example prn_6d9g2j5m8q1t4w7z0c3f6h9k2n |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Request body
application/json, required: UpdateMemberRequest
| Field | Type | Required | Description |
|---|---|---|---|
role | OrgRole | Yes | |
expected_version | integer | Yes | minimum 1 |
Example
{
"role": "issuer_administrator",
"expected_version": 1
}
Responses
| Status | Description | Body |
|---|---|---|
| 200 | The updated member (with email, as the caller is an owner). | application/json Member |
| 400 | invalid_request. Malformed JSON, an invalid or unknown field (see field_errors), a missing or malformed
Idempotency-Key, or a bad query. Also used with status 413 (body over 64 KiB) and 415 (wrong Content-Type). | application/json Error |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"principal_id": "prn_8h1k4n7q0t3w6z9c2f5j8m1p4s",
"role": "issuer_administrator",
"status": "active",
"version": 2,
"display_name": "Grace Hopper",
"idp": "google",
"email": "grace@example.com",
"created_at": "2026-10-05T11:50:00.000Z",
"updated_at": "2026-10-05T12:10:00.000Z"
}
400 example
{
"code": "invalid_request",
"message": "Invalid organization.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "legal_name",
"message": "must be 1-200 characters"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Remove a member, or leave
DELETE/v1/organizations/{id}/members/{principalId}
Removing another member needs an owner with step-up and a body { "expected_version" }. Using me as principalId is
leaving: any active human member may do it with no step-up, and expected_version is optional. The last owner can never
go (409). The departing member's service tokens for this organization are revoked and their pending invitations are
cancelled in the same commit. If they are the approved representative of a claim, that claim goes back to
pending_authority in the same commit and its issuer bindings are suspended (spec §4 material change). This DELETE takes a JSON body; send Content-Type: application/json when you include one.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
principalId | path | Yes | string | A member's principal id. DELETE also accepts the literal me.example prn_6d9g2j5m8q1t4w7z0c3f6h9k2n |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Request body
application/json: RemoveMemberRequest
expected_version is required when removing another member and optional when leaving (me).
| Field | Type | Required | Description |
|---|---|---|---|
expected_version | integer | No | minimum 1 |
Example
{
"expected_version": 2
}
Responses
| Status | Description | Body |
|---|---|---|
| 204 | Removed. | None |
| 400 | invalid_request. Malformed JSON, an invalid or unknown field (see field_errors), a missing or malformed
Idempotency-Key, or a bad query. Also used with status 413 (body over 64 KiB) and 415 (wrong Content-Type). | application/json Error |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
400 example
{
"code": "invalid_request",
"message": "Invalid organization.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "legal_name",
"message": "must be 1-200 characters"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
List pending invitations
GET/v1/organizations/{id}/invitations
Owner only. Pending, unexpired invitations the organization has sent.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | Pending invitations. | application/json OrganizationInvitationList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"invitations": [
{
"id": "inv_8h1k4n7q0t3w6z9c2f5j8m1p4s",
"role": "issuer_administrator",
"status": "pending",
"invited_by": "prn_6d9g2j5m8q1t4w7z0c3f6h9k2n",
"created_at": "2026-10-05T12:00:00.000Z",
"expires_at": "2026-10-12T12:00:00.000Z"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Invite someone by email
POST/v1/organizations/{id}/invitations
Owner, human, step-up. Consent based: nobody becomes a member until they accept. The answer is the same 202 whether or
not an account with that address exists, and never reveals a principal. Only a keyed hash of the address is stored; it is
matched against the invitee's IdP-verified email. A newer invitation to the same address supersedes the pending one. Invitations
expire after 7 days. 503 if the deployment has no invitation key configured.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Request body
application/json, required: CreateInvitationRequest
| Field | Type | Required | Description |
|---|---|---|---|
email | string (email) | Yes | at most 254 characters |
role | OrgRole | Yes |
Example
{
"email": "grace@example.com",
"role": "issuer_administrator"
}
Responses
| Status | Description | Body |
|---|---|---|
| 202 | Invitation recorded. | application/json InvitationCreated |
| 400 | invalid_request. Malformed JSON, an invalid or unknown field (see field_errors), a missing or malformed
Idempotency-Key, or a bad query. Also used with status 413 (body over 64 KiB) and 415 (wrong Content-Type). | application/json Error |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
202 example
{
"invitation_id": "inv_8h1k4n7q0t3w6z9c2f5j8m1p4s",
"organization_id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"role": "issuer_administrator",
"status": "pending",
"expires_at": "2026-10-12T12:00:00.000Z"
}
400 example
{
"code": "invalid_request",
"message": "Invalid organization.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "legal_name",
"message": "must be 1-200 characters"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Cancel a pending invitation
DELETE/v1/organizations/{id}/invitations/{invId}
Owner, human, step-up. Cancelling an already-cancelled invitation is 204; an accepted or declined one is 409.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
invId | path | Yes | string | example inv_8h1k4n7q0t3w6z9c2f5j8m1p4s |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Responses
| Status | Description | Body |
|---|---|---|
| 204 | Cancelled. | None |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
List service tokens
GET/v1/organizations/{id}/service-tokens
Owner only. Never includes secrets.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | Service tokens, oldest first. | application/json ServiceTokenList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"service_tokens": [
{
"id": "stk_1f4j7m0q3t6w9z2c5g8k1n4r7v",
"name": "ci-claims-bot",
"scopes": [
"domain_administrator"
],
"expires_at": "2026-11-04T12:00:00.000Z",
"created_by": "prn_6d9g2j5m8q1t4w7z0c3f6h9k2n",
"created_at": "2026-10-05T12:00:00.000Z",
"revoked_at": null,
"last_used_at": "2026-10-05T12:05:00.000Z"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Create a service token
POST/v1/organizations/{id}/service-tokens
Owner, human, step-up. The secret (token) is returned once; only its hash is stored, and an idempotent replay returns
the record with token: null and token_redacted: true. A service token can use the scopes you give it but is never an
owner, and it cannot step up.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Request body
application/json, required: CreateServiceTokenRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | 1 to 100 characters |
scopes | array of ServiceTokenScope | Yes | at least 1 items; unique items |
expires_in_days | integer | No | 1 to 90; default 30 |
Example
{
"name": "ci-claims-bot",
"scopes": [
"domain_administrator"
],
"expires_in_days": 30
}
Responses
| Status | Description | Body |
|---|---|---|
| 201 | The token record, including the one-time secret. Headers: Cache-Control: no-store | application/json ServiceTokenCreated |
| 400 | invalid_request. Malformed JSON, an invalid or unknown field (see field_errors), a missing or malformed
Idempotency-Key, or a bad query. Also used with status 413 (body over 64 KiB) and 415 (wrong Content-Type). | application/json Error |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
201 example
{
"id": "stk_1f4j7m0q3t6w9z2c5g8k1n4r7v",
"organization_id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"principal_id": "prn_2k5n8r1v4y7b0e3h6m9q2t5x8a",
"name": "ci-claims-bot",
"scopes": [
"domain_administrator"
],
"expires_at": "2026-11-04T12:00:00.000Z",
"created_by": "prn_6d9g2j5m8q1t4w7z0c3f6h9k2n",
"created_at": "2026-10-05T12:00:00.000Z",
"revoked_at": null,
"last_used_at": null,
"token": "art_st_0Yq3Lw8Zk1Vb5Nn9Xc2Ta6Re4Hs7Dm0Pj"
}
400 example
{
"code": "invalid_request",
"message": "Invalid organization.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w",
"field_errors": [
{
"field": "legal_name",
"message": "must be 1-200 characters"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Revoke a service token
DELETE/v1/organizations/{id}/service-tokens/{tokenId}
Owner, human. Deliberately no step-up: revocation only removes access and must stay fast in an incident. Revoking an
already-revoked token is 204.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | OrganizationId | Organization id. |
tokenId | path | Yes | string | example stk_1f4j7m0q3t6w9z2c5g8k1n4r7v |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Responses
| Status | Description | Body |
|---|---|---|
| 204 | Revoked. | None |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Invitations addressed to me
GET/v1/invitations
Human only. Pending, unexpired invitations whose inviter is still an active owner, matched on the hash of the caller's IdP-verified email. Empty if the caller's principal has no email.
Responses
| Status | Description | Body |
|---|---|---|
| 200 | My invitations. | application/json MyInvitationList |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"invitations": [
{
"id": "inv_8h1k4n7q0t3w6z9c2f5j8m1p4s",
"organization": {
"id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"legal_name": "Example Payments, Inc."
},
"role": "issuer_administrator",
"created_at": "2026-10-05T12:00:00.000Z",
"expires_at": "2026-10-12T12:00:00.000Z"
}
]
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Accept an invitation
POST/v1/invitations/{id}/accept
Step-up: joining an organization grants authority, so it needs a fresh passkey assertion. Every miss (unknown, not yours,
expired, inviter no longer an owner) is the same 404. Platform staff accounts cannot accept (403).
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string | example inv_8h1k4n7q0t3w6z9c2f5j8m1p4s |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | You are now a member. | application/json InvitationAccepted |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 403 | forbidden. Authenticated but not allowed: a role that does not permit the action, a service token on a human-only operation,
a missing or wrong CSRF token, or a request Origin that is not allowed. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
200 example
{
"organization_id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
"role": "issuer_administrator",
"status": "active"
}
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
403 example
{
"code": "forbidden",
"message": "Your role does not permit this action.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
Decline an invitation
POST/v1/invitations/{id}/decline
No step-up needed. Every miss is the same 404.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string | example inv_8h1k4n7q0t3w6z9c2f5j8m1p4s |
Idempotency-Key | header | Yes | string | Unique key for this logical mutation (1 to 255 characters of A-Z a-z 0-9 . _ : -). Replays within 24 hours return the original outcome.1 to 255 characters; pattern ^[A-Za-z0-9._:-]+$example 7f3c1b52-9d44-4a0e-8f21-0a6e5b9d3c77 |
x-art-csrf | header | Yes | string | The csrf_token from GET /auth/session. Required on every session-authenticated mutation.example Kk3jVq0bYw2p0mT1aZc9sD8eF4gH6iJ7nL5oP2rQ1uA |
Responses
| Status | Description | Body |
|---|---|---|
| 204 | Declined. | None |
| 401 | unauthenticated (no or invalid credentials, or the session has not completed the passkey ceremony) or
step_up_required (the passkey assertion is too old; also returned to service tokens, which can never step up).
Both are 401; tell them apart by code. | application/json Error |
| 404 | not_found. No such resource, or one the caller may not see (the two are indistinguishable by design). | application/json Error |
| 409 | conflict. The record changed since you read it (stale expected_version), the action is not valid in the record's
current state, the Idempotency-Key was reused with a different request, or the same request is still in flight. | application/json Error |
| 503 | unavailable (temporarily unavailable; also a feature that is not configured in this deployment) or
provider_unavailable (an identity provider is not configured). Both are 503. During maintenance (for example a database
recovery) every mutating request answers unavailable "The directory is under maintenance; try again later." Reads keep working. | application/json Error |
401 unauthenticated
{
"code": "unauthenticated",
"message": "Sign in required.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
401 step_up_required
{
"code": "step_up_required",
"message": "Recent passkey verification required for this action.",
"request_id": "req_5a8c1f4h7k0m3p6s9v2x5z8b1d"
}
404 example
{
"code": "not_found",
"message": "No such resource.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
409 example
{
"code": "conflict",
"message": "The record changed since you read it; reload and retry with the current version.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}
503 example
{
"code": "unavailable",
"message": "Temporarily unavailable.",
"request_id": "req_3m6p9s2v5y8b1e4h7k0n3q6t9w"
}