# API reference

*Generated by `pnpm contract:generate` from the mounted routes, the zod schemas they parse with, and the scope and error definitions. Do not edit by hand.*

## Scopes

A newly minted project key receives every scope below. `GET /v1/whoami` is the authority for the key you hold.

- `application-accounts:write`
- `application-workspaces:write`
- `tenants:write`
- `principals:write`
- `events:write`
- `events:read`
- `capabilities:write`
- `connections:write`
- `learning:read`
- `learning:write`
- `proposals:write`
- `trials:write`
- `work:read`
- `work:write`
- `edge:register`
- `uncertainty:decide`
- `corrections:write`
- `metrics:read`
- `proposals:read`
- `receipts:read`
- `capabilities:read`
- `webhooks:write`
- `export:read`
- `erase:write`

## Routes

Every `/v1` route requires the credential named in the auth column. Project-key routes also name their required scope; Edge data-plane routes use the tenant-bound runtime key returned at registration.

### Application accounts

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/application-accounts/signup` | Start email verification for an account in this application and environment. | `application-accounts:write` |
| `POST /v1/application-accounts/resend` | Replace the pending signup code within the email send limit. | `application-accounts:write` |
| `POST /v1/application-accounts/verify` | Verify email and issue a revocable application session. | `application-accounts:write` |
| `POST /v1/application-accounts/recovery/start` | Send a code to recover an existing application account. | `application-accounts:write` |
| `POST /v1/application-accounts/recovery/verify` | Recover the scoped account and issue a recovery session. | `application-accounts:write` |
| `POST /v1/application-sessions/sign-in` | Sign in to this application and environment. | `application-accounts:write` |
| `POST /v1/application-sessions/current` | Read the current application session without returning its secret. | `application-accounts:write + x-munky-application-session` |
| `POST /v1/application-sessions/revoke` | Revoke the current application session. | `application-accounts:write + x-munky-application-session` |
| `POST /v1/application-sessions/revoke-all` | Revoke every session for this account in this application and environment. | `application-accounts:write + x-munky-application-session` |
| `POST /v1/application-accounts/password` | Replace the password and revoke all account sessions. | `application-accounts:write + x-munky-application-session` |
| `DELETE /v1/application-accounts/current` | Delete the current application account after password or recent recovery proof; owned workspaces must be erased first. | `application-accounts:write + x-munky-application-session` |

### Mobile companion

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/application-mobile-devices` | Register a companion installation and rotate its mobile credential. | `application-workspaces:write + x-munky-application-session` |
| `GET /v1/application-mobile-devices` | List the account’s companion devices without credentials or push tokens. | `application-workspaces:write + x-munky-application-session` |
| `GET /v1/application-mobile-devices/current` | Validate the current application session and mobile credential together. | `application-workspaces:write + x-munky-application-session + x-munky-mobile-device` |
| `DELETE /v1/application-mobile-devices/:deviceId` | Revoke a companion credential and clear its push token. | `application-workspaces:write + x-munky-application-session` |
| `GET /v1/application-workspaces/:workspaceId/mobile-decisions` | Read the latest 50 decisions addressed to this workspace principal. | `application-workspaces:write + x-munky-application-session + x-munky-mobile-device` |
| `GET /v1/application-workspaces/:workspaceId/mobile-decisions/:requestId` | Read one exact decision; answers use its SMS code or secure link. | `application-workspaces:write + x-munky-application-session + x-munky-mobile-device` |
| `POST /v1/apps/:appId/mobile-devices` | Register a companion installation and rotate its mobile credential. | `configured native application; x-munky-application-session` |
| `GET /v1/apps/:appId/mobile-devices` | List the account’s companion devices without credentials or push tokens. | `configured native application; x-munky-application-session` |
| `GET /v1/apps/:appId/mobile-devices/current` | Validate the current application session and mobile credential together. | `configured native application; x-munky-application-session + x-munky-mobile-device` |
| `DELETE /v1/apps/:appId/mobile-devices/:deviceId` | Revoke a companion credential and clear its push token. | `configured native application; x-munky-application-session` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/mobile-decisions` | Read the latest 50 decisions addressed to this workspace principal. | `configured native application; x-munky-application-session + x-munky-mobile-device` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/mobile-decisions/:requestId` | Read one exact decision; answers use its SMS code or secure link. | `configured native application; x-munky-application-session + x-munky-mobile-device` |

### Application workspaces

| Route | Purpose | Auth |
| --- | --- | --- |
| `GET /v1/application-workspaces/:workspaceId/policy` | Read whether new work and device observation are paused. | `application-workspaces:write + x-munky-application-session` |
| `PUT /v1/application-workspaces/:workspaceId/policy` | Pause or resume new work leases and device observation as owner or admin. | `application-workspaces:write + x-munky-application-session` |
| `DELETE /v1/application-workspaces/:workspaceId` | Erase an owned workspace and revoke its device credentials. | `application-workspaces:write + x-munky-application-session` |
| `GET /v1/application-workspaces` | List workspaces where this account has active membership. | `application-workspaces:write + x-munky-application-session` |
| `POST /v1/application-workspaces` | Create a personal or business workspace and its isolated engine tenant. | `application-workspaces:write + x-munky-application-session` |
| `GET /v1/application-workspaces/:workspaceId/members` | Read the workspace roster as owner or administrator. | `application-workspaces:write + x-munky-application-session` |
| `POST /v1/application-workspaces/:workspaceId/invitations` | Create an exact-email invitation; deliver the returned token through your product. | `application-workspaces:write + x-munky-application-session` |
| `DELETE /v1/application-workspaces/:workspaceId/invitations/:invitationId` | Revoke a pending invitation. | `application-workspaces:write + x-munky-application-session` |
| `POST /v1/application-invitations/accept` | Accept a single-use invitation for this verified account. | `application-workspaces:write + x-munky-application-session` |
| `DELETE /v1/application-workspaces/:workspaceId/members/:accountId` | Remove a membership and revoke its devices. | `application-workspaces:write + x-munky-application-session` |
| `GET /v1/application-workspaces/:workspaceId/devices` | List devices visible to this workspace member. | `application-workspaces:write + x-munky-application-session` |
| `POST /v1/application-workspaces/:workspaceId/devices` | Enroll an installation for the active member; execution requires owner or admin. | `application-workspaces:write + x-munky-application-session` |
| `DELETE /v1/application-workspaces/:workspaceId/devices/:runtimeId` | Revoke an authorized workspace device. | `application-workspaces:write + x-munky-application-session` |

### Capabilities, proposals, and trials

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/capabilities` | Declare or restore a capability | `capabilities:write` |
| `GET /v1/capabilities` | List project capabilities | `capabilities:write` |
| `DELETE /v1/capabilities/:capabilityId` | Withdraw while retaining history | `capabilities:write` |
| `POST /v1/tenants/:tenantId/connections` | Register a signed-read connection | `connections:write` |
| `GET /v1/tenants/:tenantId/connections` | Connection summaries | `connections:write` |
| `GET /v1/tenants/:tenantId/case-approvals` | Pending and decided sensitive cases | `proposals:read` |
| `POST /v1/case-approvals/:approvalId/approve` | Approve one always-ask case | `trials:write` |
| `POST /v1/case-approvals/:approvalId/decline` | Decline one always-ask case | `trials:write` |
| `GET /v1/tenants/:tenantId/proposals` | Tenant proposals | `proposals:read` |
| `GET /v1/proposals/:proposalId` | One proposal | `proposals:read` |
| `POST /v1/proposals/:proposalId/approve` | Approve the exact plan and start a trial | `proposals:write` |
| `POST /v1/proposals/:proposalId/decline` | Decline with a structured reason | `proposals:write` |
| `GET /v1/tenants/:tenantId/trials` | Tenant trials | `proposals:read` |
| `GET /v1/trials/:trialId` | One trial | `proposals:read` |
| `POST /v1/trials/:trialId/stop` | Stop a trial | `trials:write` |

### Work, proof, and governance

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/tenants/:tenantId/corrections` | Append a correction and relearn | `corrections:write` |
| `GET /v1/tenants/:tenantId/corrections` | Correction history | `learning:read` |
| `GET /v1/tenants/:tenantId/work` | Lease an ordered work page | `work:read` |
| `GET /v1/work/:workItemId` | One work item | `work:read` |
| `POST /v1/work/:workItemId/report` | Report performed, refused, or failed | `work:write` |
| `GET /v1/tenants/:tenantId/receipts` | Tenant receipts | `receipts:read` |
| `GET /v1/receipts/:receiptId` | One coherent receipt | `receipts:read` |
| `GET /v1/tenants/:tenantId/export` | Tenant data and evidence export | `export:read` |
| `GET /v1/audit/export` | Security audit and erasure export | `export:read` |
| `DELETE /v1/tenants/:tenantId` | Idempotent tenant erasure | `erase:write` |
| `GET /v1/tenants/:tenantId/metrics` | Tenant work metrics | `metrics:read` |
| `GET /v1/metrics` | Project aggregate work metrics | `metrics:read` |
| `GET /v1/usage` | Usage and plan records | `project key` |
| `POST /v1/tenants/:tenantId/text-decisions` | Persist an immutable numbered or Yes/No decision request; does not send messages or approve work. | `proposals:write` |
| `GET /v1/tenants/:tenantId/text-decisions/:requestId` | Read the exact prompt, delivery state and recorded selection. | `proposals:read` |
| `POST /v1/tenants/:tenantId/text-decisions/:requestId/cancel` | Cancel an unanswered decision bound to its displayed prompt. | `proposals:write` |
| `POST /v1/webhooks` | Register a signed project webhook; the secret is returned once | `webhooks:write` |
| `GET /v1/webhooks` | Webhook summaries, secrets omitted | `webhooks:write` |
| `DELETE /v1/webhooks/:endpointId` | Remove a webhook; delivery history is kept | `webhooks:write` |
| `GET /v1/webhooks/deliveries` | Recent delivery attempts | `webhooks:write` |
| `POST /v1/webhooks/deliveries/:eventId/replay` | Replay one eligible delivery by its UUID event id | `webhooks:write` |

### Native application gateway

| Route | Purpose | Auth |
| --- | --- | --- |
| `GET /v1/apps/:appId/status` | Check configured native application availability | `none; configured native application` |
| `POST /v1/apps/:appId/accounts/signup` | Start native application signup | `none; configured native application` |
| `POST /v1/apps/:appId/accounts/resend` | Resend native signup verification | `none; configured native application` |
| `POST /v1/apps/:appId/accounts/verify` | Verify native signup and issue an application session | `none; configured native application` |
| `POST /v1/apps/:appId/accounts/recovery/start` | Start native account recovery | `none; configured native application` |
| `POST /v1/apps/:appId/accounts/recovery/verify` | Verify native account recovery | `none; configured native application` |
| `POST /v1/apps/:appId/sessions/sign-in` | Sign in to the native application | `none; configured native application` |
| `POST /v1/apps/:appId/sessions/current` | Read the current native application session | `x-munky-application-session; configured native application` |
| `POST /v1/apps/:appId/sessions/revoke` | Sign out the current native application session | `x-munky-application-session; configured native application` |
| `GET /v1/apps/:appId/workspaces` | Read native workspace workspace | `x-munky-application-session; configured native application` |
| `POST /v1/apps/:appId/workspaces` | Create or submit native workspace workspace | `x-munky-application-session; configured native application` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/policy` | Read native workspace policy | `x-munky-application-session; configured native application` |
| `PUT /v1/apps/:appId/workspaces/:workspaceId/policy` | Update native workspace policy | `x-munky-application-session; configured native application` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/devices` | Read native workspace devices | `x-munky-application-session; configured native application` |
| `POST /v1/apps/:appId/workspaces/:workspaceId/devices` | Create or submit native workspace devices | `x-munky-application-session; configured native application` |
| `DELETE /v1/apps/:appId/workspaces/:workspaceId/devices/:runtimeId` | Revoke native workspace :runtimeId | `x-munky-application-session; configured native application` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/phone-contact` | Read native workspace phone-contact | `x-munky-application-session; configured native application` |
| `DELETE /v1/apps/:appId/workspaces/:workspaceId/phone-contact` | Revoke native workspace phone-contact | `x-munky-application-session; configured native application` |
| `POST /v1/apps/:appId/workspaces/:workspaceId/phone-challenges` | Create or submit native workspace phone-challenges | `x-munky-application-session; configured native application` |
| `POST /v1/apps/:appId/workspaces/:workspaceId/phone-challenges/:challengeId/verify` | Create or submit native workspace verify | `x-munky-application-session; configured native application` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/interruptions` | Read native workspace interruptions | `x-munky-application-session; configured native application` |
| `GET /v1/apps/:appId/workspaces/:workspaceId/receipts` | Read native workspace receipts | `x-munky-application-session; configured native application` |

### Edge runtimes and uncertainty

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/tenants/:tenantId/edge-runtimes` | Register a tenant-bound Edge runtime and return its credential once | `edge:register` |
| `GET /v1/tenants/:tenantId/edge-runtimes` | List the tenant's registered Edge runtimes | `edge:register` |
| `DELETE /v1/edge-runtimes/:runtimeId` | Revoke an Edge runtime and its credential | `edge:register` |
| `POST /v1/edge-runtimes/:runtimeId/heartbeat` | Report runtime and adapter health; rotate a near-expiry credential | `runtime key` |
| `POST /v1/edge-runtimes/:runtimeId/events` | Submit minimized events from a declared observation source | `runtime key` |
| `GET /v1/edge-runtimes/:runtimeId/work` | Lease work matching the runtime's declared execution adapters | `runtime key` |
| `POST /v1/edge-runtimes/:runtimeId/work/:workItemId/report` | Report a bounded Edge assignment as performed, refused, or failed | `runtime key` |
| `POST /v1/edge-runtimes/:runtimeId/work/:workItemId/uncertainties` | Pause an assignment and raise structural uncertainty for a person | `runtime key` |
| `GET /v1/edge-runtimes/:runtimeId/work-uncertainties` | List uncertainty records visible to this runtime | `runtime key` |
| `POST /v1/work-uncertainties/:uncertaintyId/resolve` | Resolve uncertainty with an exact delegated-principal decision | `uncertainty:decide` |
| `POST /v1/edge-runtimes/:runtimeId/frame-escalations` | Have one redacted frame read by a provider when the runtime's own tier cannot | `runtime key` |

### Edge runtime

| Route | Purpose | Auth |
| --- | --- | --- |
| `GET /v1/tenants/:tenantId/work-uncertainties` | Read a workspace interruption inbox. Query status defaults to open; limit is 1–50; pass nextCursor as cursor for the next page. Requires a trusted backend; no runtime key is needed. | `uncertainty:decide` |

### Evidence and learning

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/tenants/:tenantId/labels` | Human evaluation label | `learning:write` |
| `GET /v1/tenants/:tenantId/labels` | Label history | `learning:read` |
| `GET /v1/tenants/:tenantId/accuracy` | Label-backed quality gate | `learning:read` |
| `POST /v1/tenants/:tenantId/events` | Live event or batch intake (Edge envelopes only) | `events:write` |
| `POST /v1/tenants/:tenantId/events/import` | Historical intake without replay proposals | `events:write` |
| `GET /v1/tenants/:tenantId/events` | Events, with since (ISO 8601) and limit (1 to 1,000) | `events:read` |
| `GET /v1/tenants/:tenantId/events/unmapped` | Unknown event shapes | `events:read` |
| `POST /v1/tenants/:tenantId/events/aliases` | Confirm tenant-specific semantics | `events:write` |
| `GET /v1/tenants/:tenantId/episodes` | Reconstructed episodes | `learning:read` |
| `POST /v1/tenants/:tenantId/learn` | Run a learning pass | `learning:write` |
| `GET /v1/tenants/:tenantId/learning` | Status and recent passes | `learning:read` |
| `GET /v1/tenants/:tenantId/learning-feedback` | Outcome, correction, drift, and proposal feedback | `learning:read` |
| `GET /v1/tenants/:tenantId/jobs` | Latest job-family summaries | `learning:read` |
| `GET /v1/tenants/:tenantId/jobs/:familyId/versions` | Contract history | `learning:read` |
| `GET /v1/tenants/:tenantId/jobs/:familyId/episodes` | Evidence for one family | `learning:read` |
| `GET /v1/tenants/:tenantId/jobs/:familyId` | Latest versioned job contract | `learning:read` |

### Identity and tenants

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/tenants` | Idempotent tenant upsert | `tenants:write` |
| `GET /v1/tenants` | Project and environment tenant list | `tenants:write` |
| `GET /v1/tenants/:tenantId` | One tenant mapping | `tenants:write` |
| `POST /v1/tenants/:tenantId/principals` | Principal upsert with permissions | `principals:write` |
| `GET /v1/tenants/:tenantId/principals` | Tenant principal list | `principals:write` |
| `GET /v1/whoami` | Caller, scopes, environment, and available areas | `project key` |

### System

| Route | Purpose | Auth |
| --- | --- | --- |
| `GET /health` | Process liveness. No key. | none |
| `GET /ready` | Database readiness. No key. | none |
| `GET /docs/integration.md` | Public integration guide and complete API reference | none |
| `GET /docs/INTEGRATION.md` | Read public integration documentation. No key. | none |
| `GET /docs/API-REFERENCE.md` | Read public integration documentation. No key. | none |
| `GET /docs/LOCAL-RUNTIME.md` | Read public integration documentation. No key. | none |
| `GET /docs/public-contract.json` | Read public integration documentation. No key. | none |
| `GET /sdk/munky-sdk-0.1.0.tgz` | Download the standalone SDK. No key. | none |
| `GET /runtime/munky-local-runtime-0.1.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.1.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /sdk/munky-sdk-0.2.0.tgz` | Download the standalone SDK. No key. | none |
| `GET /runtime/munky-local-runtime-0.2.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.2.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /sdk/munky-sdk-0.3.0.tgz` | Download the standalone SDK. No key. | none |
| `GET /runtime/munky-local-runtime-0.3.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.3.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /sdk/munky-sdk-0.4.0.tgz` | Download the SDK with numbered and Yes/No text decision contracts. No key. | none |
| `GET /sdk/munky-sdk-0.5.0.tgz` | Download the SDK with verified phone contacts and durable decision delivery APIs. No key. | none |
| `GET /runtime/munky-local-runtime-0.4.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.4.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /runtime/munky-local-runtime-0.5.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.5.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /runtime/munky-local-runtime-0.6.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.6.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /runtime/munky-local-runtime-0.7.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.7.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /runtime/munky-local-runtime-0.8.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.8.0.json` | Read the local runtime archive checksum. No key. | none |
| `GET /runtime/munky-local-runtime-0.9.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.9.0.json` | Read the local runtime checksum. No key. | none |
| `GET /runtime/munky-local-runtime-0.10.0.tgz` | Download the local runtime package. No key. | none |
| `GET /runtime/munky-local-runtime-0.10.0.json` | Read the local runtime checksum. No key. | none |
| `GET /sdk/munky-sdk-0.7.0.tgz` | Download SDK 0.7.0 with the workspace interruption inbox. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.15.0.tgz` | Download runtime 0.15.0 with native application gateway contracts. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.15.0.json` | Read the runtime 0.15.0 checksum. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.14.0.tgz` | Download runtime 0.14.0 with exact-hold retry protection. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.14.0.json` | Read the runtime 0.14.0 checksum. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.13.0.tgz` | Download local runtime 0.13.0. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.13.0.json` | Read the runtime 0.13.0 checksum. No key. | `project key` |
| `GET /sdk/munky-sdk-0.6.0.tgz` | Download the public SDK package. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.11.0.tgz` | Download the local runtime package. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.11.0.json` | Read the local runtime checksum. No key. | `project key` |
| `GET /v1/docs` | This reference as Markdown, for a key holder | `project key` |

### Distribution

| Route | Purpose | Auth |
| --- | --- | --- |
| `GET /sdk/munky-sdk-0.8.0.tgz` | Download SDK 0.8.0 with companion identity and decision reads. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.18.0.tgz` | Download runtime 0.18.0 with the direct SMS decision migration. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.18.0.json` | Read the runtime 0.18.0 checksum. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.17.0.tgz` | Download runtime 0.17.0 tgz release. No key. | `project key` |
| `GET /runtime/munky-local-runtime-0.17.0.json` | Download runtime 0.17.0 json release. No key. | `project key` |

### Text delivery

| Route | Purpose | Auth |
| --- | --- | --- |
| `POST /v1/tenants/:tenantId/principals/:principalId/phone-challenges` | Send a rate-limited phone possession challenge after messaging consent. | `principals:write` |
| `POST /v1/tenants/:tenantId/principals/:principalId/phone-challenges/:challengeId/verify` | Verify the code and bind the phone to this principal; grants no purchasing authority. | `principals:write` |
| `GET /v1/tenants/:tenantId/principals/:principalId/phone-contact` | Read verification status and the last four digits only. | `principals:write` |
| `DELETE /v1/tenants/:tenantId/principals/:principalId/phone-contact` | Revoke this phone contact and invalidate its decision links. | `principals:write` |
| `POST /v1/tenants/:tenantId/text-decisions/:requestId/send` | Queue one SMS decision link for the verified contact. | `proposals:write` |
| `GET /text-decisions/answer` | Open the decision screen without consuming or answering a request. | none |
| `POST /text-decisions/describe` | Read the exact decision using the private token delivered to a verified phone. | `private phone-delivered token` |
| `POST /text-decisions/answer` | Record a selection using the phone-delivered token; does not place an order. | `private phone-delivered token and same-origin request` |
| `POST /v1/text-decisions/sms` | Authenticate Twilio STOP/START/HELP; plain replies never select a purchase. | `Twilio signature, account and destination` |

## Request bodies

Every POST route, with its schema, an example that validates against it, and what a first call needs to know.

### `POST /v1/application-mobile-devices`

- Store the returned mobile token in Keychain. Re-registration rotates it; push delivery is not configured.

Example:

```json
{
  "installationId": "324f888c-7f83-4096-a1bd-d2ad46164ecf",
  "displayName": "My iPhone",
  "platform": "ios",
  "appVersion": "0.1.0"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "installationId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "platform": {
      "type": "string",
      "const": "ios"
    },
    "appVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "push": {
      "type": "object",
      "properties": {
        "token": {
          "type": "string",
          "pattern": "^(?:[a-fA-F0-9]{2}){32,100}$"
        },
        "environment": {
          "type": "string",
          "enum": [
            "sandbox",
            "production"
          ]
        }
      },
      "required": [
        "token",
        "environment"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "installationId",
    "displayName",
    "platform",
    "appVersion"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/mobile-devices`

- Store the returned mobile token in Keychain. Re-registration rotates it; push delivery is not configured.

Example:

```json
{
  "installationId": "324f888c-7f83-4096-a1bd-d2ad46164ecf",
  "displayName": "My iPhone",
  "platform": "ios",
  "appVersion": "0.1.0"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "installationId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "platform": {
      "type": "string",
      "const": "ios"
    },
    "appVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "push": {
      "type": "object",
      "properties": {
        "token": {
          "type": "string",
          "pattern": "^(?:[a-fA-F0-9]{2}){32,100}$"
        },
        "environment": {
          "type": "string",
          "enum": [
            "sandbox",
            "production"
          ]
        }
      },
      "required": [
        "token",
        "environment"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "installationId",
    "displayName",
    "platform",
    "appVersion"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-workspaces`

- Requires a backend project key and the x-munky-application-session header. Never put credentials or invitation tokens in URLs.

Example:

```json
{
  "kind": "personal",
  "displayName": "My work"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "personal",
        "business"
      ]
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    }
  },
  "required": [
    "kind",
    "displayName"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-workspaces/:workspaceId/invitations`

- Requires a backend project key and the x-munky-application-session header. Never put credentials or invitation tokens in URLs.

Example:

```json
{
  "email": "colleague@example.com",
  "role": "member"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "member"
      ]
    }
  },
  "required": [
    "email",
    "role"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-invitations/accept`

- Requires a backend project key and the x-munky-application-session header. Never put credentials or invitation tokens in URLs.

Example:

```json
{
  "token": "mai_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "token": {
      "type": "string",
      "pattern": "^mai_[A-Za-z0-9_-]{43}$"
    }
  },
  "required": [
    "token"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-workspaces/:workspaceId/devices`

- Requires a backend project key and the x-munky-application-session header. Never put credentials or invitation tokens in URLs.

Example:

```json
{
  "runtimeExternalId": "installation-1",
  "kind": "customer-edge",
  "platform": "macos",
  "runtimeVersion": "1.0.0",
  "protocolVersion": "attesko.edge.runtime.v1",
  "features": {
    "observations": false,
    "execution": "none",
    "observationAdapters": [],
    "executionAdapters": [],
    "uncertaintyChannels": []
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "runtimeExternalId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
    },
    "kind": {
      "type": "string",
      "enum": [
        "attesko-desktop",
        "customer-edge",
        "server-edge"
      ]
    },
    "platform": {
      "type": "string",
      "enum": [
        "macos",
        "windows",
        "linux",
        "server"
      ]
    },
    "runtimeVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "protocolVersion": {
      "type": "string",
      "const": "attesko.edge.runtime.v1"
    },
    "features": {
      "type": "object",
      "properties": {
        "observations": {
          "type": "boolean"
        },
        "execution": {
          "type": "string",
          "enum": [
            "none",
            "adapter"
          ]
        },
        "observationAdapters": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "native-event",
                  "browser-dom",
                  "os-accessibility",
                  "screen-capture"
                ]
              },
              "sourceSystems": {
                "default": [],
                "maxItems": 100,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-z][a-z0-9_.-]*$"
                }
              }
            },
            "required": [
              "kind"
            ]
          }
        },
        "executionAdapters": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "native-api",
                  "mcp",
                  "browser-dom",
                  "os-accessibility"
                ]
              },
              "capabilityIds": {
                "default": [],
                "maxItems": 200,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-z][a-z0-9_.-]*$"
                }
              },
              "attendance": {
                "type": "string",
                "enum": [
                  "attended"
                ]
              }
            },
            "required": [
              "kind"
            ]
          }
        },
        "uncertaintyChannels": {
          "default": [],
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "local-notification",
              "sms",
              "email",
              "webhook"
            ]
          }
        }
      },
      "required": [
        "observations",
        "execution"
      ]
    }
  },
  "required": [
    "runtimeExternalId",
    "kind",
    "platform",
    "runtimeVersion",
    "protocolVersion",
    "features"
  ]
}
```

</details>

### `PUT /v1/application-workspaces/:workspaceId/policy`

- Requires a backend project key and the x-munky-application-session header. Never put credentials or invitation tokens in URLs.

Example:

```json
{
  "paused": true
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "paused": {
      "type": "boolean"
    }
  },
  "required": [
    "paused"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-accounts/signup`

- Requires a backend project key with application-accounts:write.

Example:

```json
{
  "email": "person@example.com",
  "password": "example-password-long-enough"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "password": {
      "type": "string",
      "minLength": 10,
      "maxLength": 1024
    }
  },
  "required": [
    "email",
    "password"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-accounts/resend`

- Requires a backend project key with application-accounts:write.

Example:

```json
{
  "email": "person@example.com"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-accounts/verify`

- Requires a backend project key with application-accounts:write.

Example:

```json
{
  "email": "person@example.com",
  "code": "123456"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "code": {
      "type": "string",
      "pattern": "^\\d{6}$"
    }
  },
  "required": [
    "email",
    "code"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-accounts/recovery/start`

- Requires a backend project key with application-accounts:write.

Example:

```json
{
  "email": "person@example.com"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-accounts/recovery/verify`

- Requires a backend project key with application-accounts:write.

Example:

```json
{
  "email": "person@example.com",
  "code": "123456"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "code": {
      "type": "string",
      "pattern": "^\\d{6}$"
    }
  },
  "required": [
    "email",
    "code"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-sessions/sign-in`

- Requires a backend project key with application-accounts:write.

Example:

```json
{
  "email": "person@example.com",
  "password": "example-password-long-enough"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "password": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1024
    }
  },
  "required": [
    "email",
    "password"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-sessions/current`

- Requires a backend project key with application-accounts:write. Also requires the x-munky-application-session header; never put the token in a URL.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-sessions/revoke`

- Requires a backend project key with application-accounts:write. Also requires the x-munky-application-session header; never put the token in a URL.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-sessions/revoke-all`

- Requires a backend project key with application-accounts:write. Also requires the x-munky-application-session header; never put the token in a URL.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### `DELETE /v1/application-accounts/current`

- Requires the application session header. Current password may be omitted only within 15 minutes of mailbox recovery. Owned workspaces must be erased first.

Example:

```json
{
  "currentPassword": "example-password-long-enough"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "currentPassword": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1024
    }
  },
  "additionalProperties": false
}
```

</details>

### `POST /v1/application-accounts/password`

- Requires a backend project key with application-accounts:write. Also requires the x-munky-application-session header; never put the token in a URL.

Example:

```json
{
  "currentPassword": "old-example-password",
  "newPassword": "new-example-password"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "currentPassword": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1024
    },
    "newPassword": {
      "type": "string",
      "minLength": 10,
      "maxLength": 1024
    }
  },
  "required": [
    "newPassword"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/tenants/:tenantId/principals/:principalId/phone-challenges`

- Only send after the user requests phone setup. At most three challenges per phone per hour; five code guesses per challenge. Codes expire after ten minutes.

Example:

```json
{
  "phone": "+15555550123",
  "consent": true
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "phone": {
      "type": "string",
      "pattern": "^\\+[1-9][0-9]{7,14}$"
    },
    "consent": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "phone",
    "consent"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/tenants/:tenantId/principals/:principalId/phone-challenges/:challengeId/verify`

Example:

```json
{
  "code": "123456"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "pattern": "^[0-9]{6}$"
    }
  },
  "required": [
    "code"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/tenants/:tenantId/text-decisions/:requestId/send`

- Requires an existing verified contact. Returns queue state, not handset delivery. No link token is returned to the project key.

Example:

```json
{
  "bindingHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "bindingHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    }
  },
  "required": [
    "bindingHash"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /text-decisions/describe`

- Token is carried in the SMS URL fragment and POST body, never a query string. This read does not answer the question.

Example:

```json
{
  "token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "token": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{43}$"
    }
  },
  "required": [
    "token"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /text-decisions/answer`

- Use the exact displayed choice and configured HTTPS Origin. Recorded selection is not an existing case approval or purchase receipt.

Example:

```json
{
  "token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "answer": "Yes"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "token": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{43}$"
    },
    "answer": {
      "type": "string",
      "enum": [
        "1",
        "2",
        "3",
        "4",
        "Yes",
        "No"
      ]
    }
  },
  "required": [
    "token",
    "answer"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/text-decisions/sms`

- Twilio application/x-www-form-urlencoded request, not JSON. Signature covers the configured public URL and all fields. Duplicate form fields are rejected; provider message IDs are deduplicated.

Example:

```json
{
  "AccountSid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "MessageSid": "SMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "From": "+15555550123",
  "To": "+15555550124",
  "Body": "HELP"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "propertyNames": {
    "type": "string"
  },
  "additionalProperties": {
    "type": "string"
  }
}
```

</details>

### `POST /v1/tenants/:tenantId/text-decisions`

- Replace the tenant/principal IDs and timestamps with current values. Reuse the exact input for retries.
- Creation queues a record only. There is no project-key endpoint that turns a reply into spending authority.

Example:

```json
{
  "requestId": "email-choice-1",
  "tenantId": "8a522c0b-a2b4-4e56-bd77-f9322d4f83a1",
  "principalId": "497f2c2f-9fea-4028-812f-41e2a76e8f08",
  "conversationId": "request-thread-1",
  "question": "Prepare email options?",
  "context": "Your existing email will stay as it is.",
  "mode": "yes-no",
  "recommendation": null,
  "createdAt": "2026-09-06T12:00:00Z",
  "expiresAt": "2026-09-06T12:15:00Z",
  "options": [
    {
      "id": "prepare",
      "label": "Prepare options",
      "consequence": "Compare mailbox providers.",
      "intent": "preference",
      "planHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "quote": null
    },
    {
      "id": "wait",
      "label": "Wait",
      "consequence": "Leave things unchanged.",
      "intent": "decline",
      "planHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "quote": null
    }
  ]
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "requestId": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
    },
    "tenantId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "principalId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "conversationId": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
    },
    "question": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "context": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "mode": {
      "type": "string",
      "enum": [
        "numbered",
        "yes-no"
      ]
    },
    "options": {
      "minItems": 2,
      "maxItems": 4,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "consequence": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "intent": {
            "type": "string",
            "enum": [
              "preference",
              "purchase",
              "decline"
            ]
          },
          "planHash": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "quote": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300
                  },
                  "product": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10000
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "USD",
                      "EUR",
                      "GBP",
                      "CAD",
                      "AUD"
                    ]
                  },
                  "totalDueNowMinor": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 9007199254740991
                  },
                  "tax": {
                    "type": "string",
                    "enum": [
                      "included",
                      "not-applicable",
                      "unknown"
                    ]
                  },
                  "recurring": {
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "totalMinor": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 9007199254740991
                          },
                          "interval": {
                            "type": "string",
                            "enum": [
                              "month",
                              "year"
                            ]
                          },
                          "startsAt": {
                            "type": "string",
                            "format": "date-time",
                            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                          }
                        },
                        "required": [
                          "totalMinor",
                          "interval",
                          "startsAt"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "terms": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300
                  },
                  "sourceUrl": {
                    "type": "string",
                    "format": "uri"
                  },
                  "retrievedAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  }
                },
                "required": [
                  "provider",
                  "product",
                  "quantity",
                  "currency",
                  "totalDueNowMinor",
                  "tax",
                  "recurring",
                  "terms",
                  "sourceUrl",
                  "retrievedAt",
                  "expiresAt"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "label",
          "consequence",
          "intent",
          "planHash",
          "quote"
        ],
        "additionalProperties": false
      }
    },
    "recommendation": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "optionId": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
            },
            "reason": {
              "type": "string",
              "minLength": 1,
              "maxLength": 300
            }
          },
          "required": [
            "optionId",
            "reason"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    }
  },
  "required": [
    "requestId",
    "tenantId",
    "principalId",
    "conversationId",
    "question",
    "context",
    "mode",
    "options",
    "recommendation",
    "createdAt",
    "expiresAt"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/tenants/:tenantId/text-decisions/:requestId/cancel`

- Supply the bindingHash returned by create/read; this example hash is illustrative.

Example:

```json
{
  "bindingHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "bindingHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    }
  },
  "required": [
    "bindingHash"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/tenants`

Example:

```json
{
  "externalId": "customer_2048",
  "displayName": "Northwind Logistics"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "externalId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "metadata": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "externalId",
    "displayName"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/principals`

- `permissions` is a list of objects, not strings. Each names one `operation`; `capabilityIds` narrows it to an allow-list.
- `role` is your own label. Munky authorizes permissions, never role names.

Example:

```json
{
  "externalId": "user_17",
  "displayName": "Ops lead",
  "role": "billing_manager",
  "permissions": [
    {
      "operation": "proposal.approve"
    },
    {
      "operation": "case.approve",
      "capabilityIds": [
        "invoice.send_follow_up"
      ]
    }
  ]
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "externalId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "role": {
      "default": "member",
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "permissions": {
      "default": [],
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "operation": {
            "type": "string",
            "enum": [
              "proposal.approve",
              "proposal.decline",
              "trial.stop",
              "case.approve",
              "case.decline",
              "correction.apply",
              "evaluation.label",
              "uncertainty.resolve",
              "connector-job.register"
            ]
          },
          "capabilityIds": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            }
          }
        },
        "required": [
          "operation"
        ]
      }
    }
  },
  "required": [
    "externalId",
    "displayName"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/events`

- Send the envelope the Edge SDK produces. Every identifier is a `psn_v1_` pseudonym; state values that are not Munky's own vocabulary are pseudonyms too; only allow-listed field names survive.
- One envelope, or `{ "events": [ ...envelopes ] }` with 1 to 1,000 items. The route tenant is authoritative.
- Per-item rejection codes: `privacy_rejected`, `tenant_mismatch`, `schema_invalid`, `store_failed`. The request succeeds (200) whenever it is well formed; only the request itself being malformed is a 422.

Developer input (`@munky/sdk` `events.track`):

```json
{
  "tenantId": "8786c412-4591-4a47-bb0a-be5440bd4e09",
  "source": "billing",
  "type": "invoice.updated",
  "occurredAt": "2026-09-02T15:00:00.000Z",
  "actor": {
    "id": "user-382"
  },
  "subject": {
    "type": "invoice",
    "id": "invoice-8291"
  },
  "changedFields": [
    "status"
  ],
  "before": {
    "status": "overdue"
  },
  "after": {
    "status": "follow_up_sent"
  },
  "correlationId": "chase-8291"
}
```

What the SDK sends:

```json
{
  "schemaVersion": "attesko.edge.event.v1",
  "privacy": {
    "pseudonymization": "hmac-sha256",
    "keyId": "primary",
    "removedFieldCount": 0
  },
  "event": {
    "tenantId": "8786c412-4591-4a47-bb0a-be5440bd4e09",
    "principalExternalId": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "sourceSystem": "billing",
    "eventType": "invoice.updated",
    "occurredAt": "2026-09-02T15:00:00.000Z",
    "sourceRecordIds": [],
    "entities": [],
    "subjectExternalId": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "subjectType": "invoice",
    "beforeState": {
      "status": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    },
    "afterState": {
      "status": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    },
    "changedFields": [
      "status"
    ],
    "correlationId": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "idempotencyKey": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "schemaVersion": {
      "type": "string",
      "const": "attesko.edge.event.v1"
    },
    "privacy": {
      "type": "object",
      "properties": {
        "pseudonymization": {
          "type": "string",
          "const": "hmac-sha256"
        },
        "keyId": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_-]{0,99}$"
        },
        "removedFieldCount": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "pseudonymization",
        "keyId",
        "removedFieldCount"
      ]
    },
    "event": {
      "type": "object",
      "properties": {
        "tenantId": {
          "type": "string",
          "minLength": 1
        },
        "eventId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "principalExternalId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "sourceSystem": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "eventType": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "occurredAt": {
          "type": "string",
          "minLength": 1
        },
        "sourceRecordIds": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 400
          }
        },
        "entities": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              },
              "externalId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 400
              },
              "label": {
                "type": "string",
                "maxLength": 400
              }
            },
            "required": [
              "type",
              "externalId"
            ]
          }
        },
        "subjectExternalId": {
          "type": "string",
          "maxLength": 400
        },
        "subjectType": {
          "type": "string",
          "maxLength": 100
        },
        "beforeState": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        },
        "afterState": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        },
        "changedFields": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 200
          }
        },
        "correlationId": {
          "type": "string",
          "maxLength": 400
        },
        "causationId": {
          "type": "string",
          "maxLength": 400
        },
        "idempotencyKey": {
          "type": "string",
          "minLength": 1,
          "maxLength": 400
        },
        "provenance": {
          "type": "string",
          "enum": [
            "product-reported",
            "connected-system",
            "inference",
            "user-confirmed",
            "verification-evidence"
          ]
        },
        "sensitivity": {
          "type": "string",
          "enum": [
            "none",
            "business",
            "personal",
            "financial"
          ]
        },
        "metadata": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "tenantId",
        "sourceSystem",
        "eventType",
        "occurredAt",
        "idempotencyKey"
      ]
    }
  },
  "required": [
    "schemaVersion",
    "privacy",
    "event"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/events/import`

- Same body as live intake. Imports are marked historical and never raise a proposal.

Example:

```json
{
  "schemaVersion": "attesko.edge.event.v1",
  "privacy": {
    "pseudonymization": "hmac-sha256",
    "keyId": "primary",
    "removedFieldCount": 0
  },
  "event": {
    "tenantId": "8786c412-4591-4a47-bb0a-be5440bd4e09",
    "sourceSystem": "billing",
    "eventType": "invoice.sent",
    "occurredAt": "2026-06-01T10:00:00.000Z",
    "sourceRecordIds": [],
    "entities": [],
    "subjectExternalId": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "subjectType": "invoice",
    "changedFields": [],
    "idempotencyKey": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "schemaVersion": {
      "type": "string",
      "const": "attesko.edge.event.v1"
    },
    "privacy": {
      "type": "object",
      "properties": {
        "pseudonymization": {
          "type": "string",
          "const": "hmac-sha256"
        },
        "keyId": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_-]{0,99}$"
        },
        "removedFieldCount": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "pseudonymization",
        "keyId",
        "removedFieldCount"
      ]
    },
    "event": {
      "type": "object",
      "properties": {
        "tenantId": {
          "type": "string",
          "minLength": 1
        },
        "eventId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "principalExternalId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "sourceSystem": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "eventType": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "occurredAt": {
          "type": "string",
          "minLength": 1
        },
        "sourceRecordIds": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 400
          }
        },
        "entities": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              },
              "externalId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 400
              },
              "label": {
                "type": "string",
                "maxLength": 400
              }
            },
            "required": [
              "type",
              "externalId"
            ]
          }
        },
        "subjectExternalId": {
          "type": "string",
          "maxLength": 400
        },
        "subjectType": {
          "type": "string",
          "maxLength": 100
        },
        "beforeState": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        },
        "afterState": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        },
        "changedFields": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 200
          }
        },
        "correlationId": {
          "type": "string",
          "maxLength": 400
        },
        "causationId": {
          "type": "string",
          "maxLength": 400
        },
        "idempotencyKey": {
          "type": "string",
          "minLength": 1,
          "maxLength": 400
        },
        "provenance": {
          "type": "string",
          "enum": [
            "product-reported",
            "connected-system",
            "inference",
            "user-confirmed",
            "verification-evidence"
          ]
        },
        "sensitivity": {
          "type": "string",
          "enum": [
            "none",
            "business",
            "personal",
            "financial"
          ]
        },
        "metadata": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "tenantId",
        "sourceSystem",
        "eventType",
        "occurredAt",
        "idempotencyKey"
      ]
    }
  },
  "required": [
    "schemaVersion",
    "privacy",
    "event"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/events/aliases`

- `surface` and `action` are Munky's vocabulary for what the event means; `confirmedByPrincipalId` is the Munky id of the person confirming it (from a principal upsert).

Example:

```json
{
  "eventType": "invoice.chased",
  "surface": "invoice",
  "action": "submitting-or-sending",
  "confirmedByPrincipalId": "prn_2f1c…",
  "note": "Our word for sending the reminder"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "eventType": {
      "type": "string",
      "minLength": 1
    },
    "surface": {
      "type": "string",
      "minLength": 1
    },
    "action": {
      "type": "string",
      "minLength": 1
    },
    "confirmedByPrincipalId": {
      "type": "string",
      "minLength": 1
    },
    "note": {
      "type": "string",
      "maxLength": 2000
    }
  },
  "required": [
    "eventType",
    "surface",
    "action",
    "confirmedByPrincipalId"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/learn`

- No body fields. Send `{}`.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {}
}
```

</details>

### `POST /v1/tenants/:tenantId/labels`

- `kind` is one of: `same-family`, `same-variant`, `contract-intent`, `contract-inputs`, `contract-terminal-outcome`, `contract-executor`, `contract-verifier`, `contract-commit-boundary`, `contract-decision-points`, `contract-capability-mapping`, `case-eligibility`, `verifier-correctness`.
- Verdicts for `same-family`: `same-family`, `different-family`, `unsure`.
- Verdicts for `same-variant`: `same-variant`, `different-variant`, `unsure`.
- Verdicts for `contract-intent`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-inputs`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-terminal-outcome`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-executor`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-verifier`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-commit-boundary`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-decision-points`: `correct`, `incorrect`, `unsure`.
- Verdicts for `contract-capability-mapping`: `correct`, `incorrect`, `unsure`.
- Verdicts for `case-eligibility`: `correct`, `incorrect`, `unsure`.
- Verdicts for `verifier-correctness`: `correct`, `incorrect`, `unsure`.
- `subject` names what is being labelled: `{ familyId }` for a contract, `{ familyId, contractVersion }` for a field, `{ episodeIds: [a, b] }` for a pair kind.
- `principalAssertion` is a delegated-principal assertion signed with a registered Ed25519 key.

Example:

```json
{
  "kind": "contract-intent",
  "subject": {
    "familyId": "fam_7c2e…",
    "contractVersion": 1
  },
  "verdict": "correct",
  "note": "",
  "labelledByPrincipalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "minLength": 1
    },
    "subject": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "verdict": {
      "type": "string",
      "minLength": 1
    },
    "note": {
      "default": "",
      "type": "string",
      "maxLength": 2000
    },
    "labelledByPrincipalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    }
  },
  "required": [
    "kind",
    "subject",
    "verdict",
    "labelledByPrincipalId",
    "principalAssertion"
  ]
}
```

</details>

### `POST /v1/capabilities`

Example:

```json
{
  "capabilityId": "invoice.send_follow_up",
  "displayName": "Send an invoice follow-up",
  "performs": {
    "action": "send",
    "entityType": "invoice",
    "surface": "billing"
  },
  "idempotent": true,
  "customerFacing": true,
  "moneyMoving": false,
  "reversible": false,
  "argumentSchema": {
    "type": "object",
    "required": [
      "invoiceId"
    ]
  },
  "argumentBindings": {
    "invoiceId": {
      "from": "subject.externalId"
    }
  },
  "verifier": {
    "kind": "PRODUCT_EVENT",
    "eventType": "invoice.updated",
    "windowMinutes": 60,
    "expect": [
      {
        "field": "status",
        "expect": "changed"
      }
    ]
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "capabilityId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "pattern": "^[a-z][a-z0-9_\\-.]*$"
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "performs": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "minLength": 1
        },
        "entityType": {
          "type": "string",
          "minLength": 1
        },
        "surface": {
          "type": "string",
          "minLength": 1
        }
      },
      "required": [
        "action",
        "entityType"
      ]
    },
    "idempotent": {
      "type": "boolean"
    },
    "customerFacing": {
      "type": "boolean"
    },
    "moneyMoving": {
      "type": "boolean"
    },
    "reversible": {
      "default": false,
      "type": "boolean"
    },
    "argumentSchema": {
      "default": {
        "type": "object"
      },
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "argumentBindings": {
      "default": {},
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "const": "subject.externalId"
              }
            },
            "required": [
              "from"
            ]
          },
          {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "const": "event.afterState"
              },
              "field": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "from",
              "field"
            ]
          },
          {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "const": "event.beforeState"
              },
              "field": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "from",
              "field"
            ]
          },
          {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "const": "event.metadata"
              },
              "field": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "from",
              "field"
            ]
          },
          {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "const": "entity"
              },
              "entityType": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "from",
              "entityType"
            ]
          },
          {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "const": "constant"
              },
              "value": {}
            },
            "required": [
              "from",
              "value"
            ]
          }
        ]
      }
    },
    "verifier": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "PRODUCT_EVENT"
            },
            "eventType": {
              "type": "string",
              "minLength": 1
            },
            "windowMinutes": {
              "default": 60,
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 1440
            },
            "expect": {
              "default": [],
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "field": {
                    "type": "string",
                    "minLength": 1
                  },
                  "expect": {
                    "anyOf": [
                      {
                        "type": "string",
                        "const": "changed"
                      },
                      {
                        "type": "string",
                        "const": "present"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "equals": {}
                        },
                        "required": [
                          "equals"
                        ]
                      },
                      {
                        "type": "object",
                        "properties": {
                          "equalsArgument": {
                            "type": "string",
                            "minLength": 1
                          }
                        },
                        "required": [
                          "equalsArgument"
                        ]
                      }
                    ]
                  }
                },
                "required": [
                  "field",
                  "expect"
                ]
              }
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "SIGNED_HTTP_READ"
            },
            "connectionId": {
              "type": "string",
              "minLength": 1
            },
            "operation": {
              "type": "string",
              "minLength": 1
            },
            "recordPath": {
              "type": "string"
            },
            "expect": {
              "default": [],
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "field": {
                    "type": "string",
                    "minLength": 1
                  },
                  "expect": {
                    "anyOf": [
                      {
                        "type": "string",
                        "const": "changed"
                      },
                      {
                        "type": "string",
                        "const": "present"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "equals": {}
                        },
                        "required": [
                          "equals"
                        ]
                      },
                      {
                        "type": "object",
                        "properties": {
                          "equalsArgument": {
                            "type": "string",
                            "minLength": 1
                          }
                        },
                        "required": [
                          "equalsArgument"
                        ]
                      }
                    ]
                  }
                },
                "required": [
                  "field",
                  "expect"
                ]
              }
            }
          },
          "required": [
            "kind",
            "connectionId",
            "operation"
          ]
        }
      ]
    }
  },
  "required": [
    "capabilityId",
    "displayName",
    "performs",
    "idempotent",
    "customerFacing",
    "moneyMoving",
    "verifier"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/connections`

- `signingSecret` is sealed on receipt and never returned.

Example:

```json
{
  "connectionId": "billing-read",
  "displayName": "Billing read API",
  "provider": "northwind",
  "baseUrl": "https://api.northwind.example",
  "operations": {
    "invoice": "/invoices/{id}"
  },
  "allowedOperations": [
    "invoice"
  ],
  "signingSecret": "a-signing-secret-of-at-least-32-characters"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "connectionId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "provider": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "baseUrl": {
      "type": "string",
      "format": "uri"
    },
    "operations": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string",
        "pattern": "^\\/.*"
      }
    },
    "allowedOperations": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "healthPath": {
      "default": "/health",
      "type": "string",
      "pattern": "^\\/.*"
    },
    "signingSecret": {
      "type": "string",
      "minLength": 32
    },
    "scopes": {
      "default": [],
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "externalAccountId": {
      "type": "string"
    },
    "timeoutMs": {
      "default": 10000,
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 120000
    }
  },
  "required": [
    "displayName",
    "provider",
    "baseUrl",
    "operations",
    "allowedOperations",
    "signingSecret"
  ]
}
```

</details>

### `POST /v1/proposals/:proposalId/approve`

Example:

```json
{
  "planHash": "sha256:9f2c…",
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "trial": {
    "maxCases": 20,
    "expiresAt": "2026-09-09T15:00:00.000Z",
    "reviewMinutesPerCase": 1
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "planHash": {
      "type": "string",
      "minLength": 1
    },
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    },
    "trial": {
      "type": "object",
      "properties": {
        "maxCases": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 1000
        },
        "expiresAt": {
          "type": "string"
        },
        "reviewMinutesPerCase": {
          "default": 0,
          "type": "number",
          "minimum": 0,
          "maximum": 600
        }
      },
      "required": [
        "maxCases"
      ]
    }
  },
  "required": [
    "planHash",
    "principalId",
    "principalAssertion",
    "trial"
  ]
}
```

</details>

### `POST /v1/proposals/:proposalId/decline`

- `reason` is one of: `not-this-job`, `wrong-path`, `not-safe-yet`, `wrong-capability`, `later`, `other`, `wrong-job`, `wrong-normal-path`, `unsafe`, `not-valuable`, `wrong-capability-mapping`, `wrong-timing`, `customer-policy`, `not-ready`.

Example:

```json
{
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "reason": "not-safe-yet",
  "note": "Wait for the new verifier"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    },
    "reason": {
      "type": "string",
      "enum": [
        "not-this-job",
        "wrong-path",
        "not-safe-yet",
        "wrong-capability",
        "later",
        "other",
        "wrong-job",
        "wrong-normal-path",
        "unsafe",
        "not-valuable",
        "wrong-capability-mapping",
        "wrong-timing",
        "customer-policy",
        "not-ready"
      ]
    },
    "note": {
      "type": "string",
      "maxLength": 2000
    }
  },
  "required": [
    "principalId",
    "principalAssertion",
    "reason"
  ]
}
```

</details>

### `POST /v1/trials/:trialId/stop`

Example:

```json
{
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "note": "Pausing during the audit"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    },
    "note": {
      "type": "string",
      "maxLength": 2000
    }
  },
  "required": [
    "principalId",
    "principalAssertion"
  ]
}
```

</details>

### `POST /v1/case-approvals/:approvalId/approve`

Example:

```json
{
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "planHash": "sha256:9f2c…",
  "reviewMinutes": 2
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    },
    "planHash": {
      "type": "string",
      "minLength": 1
    },
    "reviewMinutes": {
      "default": 0,
      "type": "number",
      "minimum": 0
    },
    "note": {
      "type": "string",
      "maxLength": 2000
    }
  },
  "required": [
    "principalId",
    "principalAssertion",
    "planHash"
  ]
}
```

</details>

### `POST /v1/case-approvals/:approvalId/decline`

Example:

```json
{
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "planHash": "sha256:9f2c…",
  "note": "Customer asked us to hold"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    },
    "planHash": {
      "type": "string",
      "minLength": 1
    },
    "reviewMinutes": {
      "default": 0,
      "type": "number",
      "minimum": 0
    },
    "note": {
      "type": "string",
      "maxLength": 2000
    }
  },
  "required": [
    "principalId",
    "principalAssertion",
    "planHash"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/edge-runtimes`

- The response returns a short-lived `attsk_rt_` runtime credential once. Store it only in the runtime's local secret store.
- Adapter declarations describe reach; they do not establish that a browser or native actuator exists.
- Every declared execution adapter must carry `"attendance": "attended"`. That is the only value: no rung has earned unattended use, and an adapter that states nothing is refused here and dispatched no work.

Example:

```json
{
  "runtimeExternalId": "install-6f4d2a",
  "kind": "customer-edge",
  "platform": "macos",
  "runtimeVersion": "1.0.0",
  "protocolVersion": "attesko.edge.runtime.v1",
  "features": {
    "observations": true,
    "execution": "none",
    "observationAdapters": [
      {
        "kind": "native-event",
        "sourceSystems": [
          "billing"
        ]
      }
    ],
    "executionAdapters": [],
    "uncertaintyChannels": [
      "local-notification"
    ]
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "runtimeExternalId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
    },
    "kind": {
      "type": "string",
      "enum": [
        "attesko-desktop",
        "customer-edge",
        "server-edge"
      ]
    },
    "platform": {
      "type": "string",
      "enum": [
        "macos",
        "windows",
        "linux",
        "server"
      ]
    },
    "runtimeVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "protocolVersion": {
      "type": "string",
      "const": "attesko.edge.runtime.v1"
    },
    "features": {
      "type": "object",
      "properties": {
        "observations": {
          "type": "boolean"
        },
        "execution": {
          "type": "string",
          "enum": [
            "none",
            "adapter"
          ]
        },
        "observationAdapters": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "native-event",
                  "browser-dom",
                  "os-accessibility",
                  "screen-capture"
                ]
              },
              "sourceSystems": {
                "default": [],
                "maxItems": 100,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-z][a-z0-9_.-]*$"
                }
              }
            },
            "required": [
              "kind"
            ]
          }
        },
        "executionAdapters": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "native-api",
                  "mcp",
                  "browser-dom",
                  "os-accessibility"
                ]
              },
              "capabilityIds": {
                "default": [],
                "maxItems": 200,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-z][a-z0-9_.-]*$"
                }
              },
              "attendance": {
                "type": "string",
                "enum": [
                  "attended"
                ]
              }
            },
            "required": [
              "kind"
            ]
          }
        },
        "uncertaintyChannels": {
          "default": [],
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "local-notification",
              "sms",
              "email",
              "webhook"
            ]
          }
        }
      },
      "required": [
        "observations",
        "execution"
      ]
    }
  },
  "required": [
    "runtimeExternalId",
    "kind",
    "platform",
    "runtimeVersion",
    "protocolVersion",
    "features"
  ]
}
```

</details>

### `POST /v1/edge-runtimes/:runtimeId/heartbeat`

- Authenticate with the runtime credential, not a project key.

Example:

```json
{
  "observedAt": "2026-09-03T15:00:00.000Z",
  "runtimeVersion": "1.0.0",
  "state": "online",
  "adapterHealth": [
    {
      "kind": "native-event",
      "state": "ready"
    }
  ],
  "activeAssignmentIds": []
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "observedAt": {
      "type": "string"
    },
    "runtimeVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "state": {
      "type": "string",
      "enum": [
        "online",
        "degraded",
        "paused"
      ]
    },
    "adapterHealth": {
      "default": [],
      "maxItems": 40,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "kind": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "native-event",
                  "browser-dom",
                  "os-accessibility",
                  "screen-capture"
                ]
              },
              {
                "type": "string",
                "enum": [
                  "native-api",
                  "mcp",
                  "browser-dom",
                  "os-accessibility"
                ]
              }
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "ready",
              "degraded",
              "unavailable"
            ]
          },
          "reasonCode": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-z][a-z0-9_.-]*$"
          }
        },
        "required": [
          "kind",
          "state"
        ],
        "additionalProperties": false
      }
    },
    "activeAssignmentIds": {
      "default": [],
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 200,
        "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
      }
    }
  },
  "required": [
    "observedAt",
    "runtimeVersion",
    "state"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/edge-runtimes/:runtimeId/events`

- Authenticate with the runtime credential. The route tenant is bound by that credential, and the source system must have been declared at registration.
- One envelope, or `{ "events": [ ...envelopes ] }` with 1 to 1,000 items.

Example:

```json
{
  "schemaVersion": "attesko.edge.event.v1",
  "privacy": {
    "pseudonymization": "hmac-sha256",
    "keyId": "primary",
    "removedFieldCount": 0
  },
  "event": {
    "tenantId": "8786c412-4591-4a47-bb0a-be5440bd4e09",
    "sourceSystem": "billing",
    "eventType": "invoice.updated",
    "occurredAt": "2026-09-03T15:00:00.000Z",
    "sourceRecordIds": [],
    "entities": [],
    "subjectExternalId": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "subjectType": "invoice",
    "changedFields": [
      "status"
    ],
    "idempotencyKey": "psn_v1_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "schemaVersion": {
      "type": "string",
      "const": "attesko.edge.event.v1"
    },
    "privacy": {
      "type": "object",
      "properties": {
        "pseudonymization": {
          "type": "string",
          "const": "hmac-sha256"
        },
        "keyId": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_-]{0,99}$"
        },
        "removedFieldCount": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "pseudonymization",
        "keyId",
        "removedFieldCount"
      ]
    },
    "event": {
      "type": "object",
      "properties": {
        "tenantId": {
          "type": "string",
          "minLength": 1
        },
        "eventId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "principalExternalId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "sourceSystem": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "eventType": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "occurredAt": {
          "type": "string",
          "minLength": 1
        },
        "sourceRecordIds": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 400
          }
        },
        "entities": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              },
              "externalId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 400
              },
              "label": {
                "type": "string",
                "maxLength": 400
              }
            },
            "required": [
              "type",
              "externalId"
            ]
          }
        },
        "subjectExternalId": {
          "type": "string",
          "maxLength": 400
        },
        "subjectType": {
          "type": "string",
          "maxLength": 100
        },
        "beforeState": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        },
        "afterState": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        },
        "changedFields": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 200
          }
        },
        "correlationId": {
          "type": "string",
          "maxLength": 400
        },
        "causationId": {
          "type": "string",
          "maxLength": 400
        },
        "idempotencyKey": {
          "type": "string",
          "minLength": 1,
          "maxLength": 400
        },
        "provenance": {
          "type": "string",
          "enum": [
            "product-reported",
            "connected-system",
            "inference",
            "user-confirmed",
            "verification-evidence"
          ]
        },
        "sensitivity": {
          "type": "string",
          "enum": [
            "none",
            "business",
            "personal",
            "financial"
          ]
        },
        "metadata": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "tenantId",
        "sourceSystem",
        "eventType",
        "occurredAt",
        "idempotencyKey"
      ]
    }
  },
  "required": [
    "schemaVersion",
    "privacy",
    "event"
  ]
}
```

</details>

### `POST /v1/edge-runtimes/:runtimeId/work/:workItemId/report`

- Authenticate with the runtime credential. `performed` is a report, not success; the core verifies independently.

Example:

```json
{
  "assignmentId": "assignment-8841",
  "bindingHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "outcome": "performed",
  "reference": "email_8841",
  "artifact": "Reminder sent for invoice 8291",
  "performedAt": "2026-09-03T15:01:12.000Z"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "outcome": {
      "type": "string",
      "enum": [
        "performed",
        "refused",
        "failed"
      ]
    },
    "reference": {
      "type": "string",
      "maxLength": 400
    },
    "artifact": {
      "type": "string",
      "maxLength": 4000
    },
    "detail": {
      "type": "string",
      "maxLength": 4000
    },
    "performedAt": {
      "type": "string"
    },
    "assignmentId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
    },
    "bindingHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    }
  },
  "required": [
    "outcome",
    "assignmentId",
    "bindingHash"
  ]
}
```

</details>

### `POST /v1/edge-runtimes/:runtimeId/work/:workItemId/uncertainties`

- Send structural identifiers and reason codes only; do not send screenshots, DOM text, document content, or exception messages.

Example:

```json
{
  "assignmentId": "assignment-8841",
  "bindingHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "reason": "target-ambiguous",
  "reasonCode": "multiple-matching-records",
  "context": {
    "requiredFieldNames": [],
    "optionIds": [
      "record-1",
      "record-2"
    ],
    "candidateCount": 2
  },
  "observedAt": "2026-09-03T15:01:00.000Z"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "assignmentId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
    },
    "bindingHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    },
    "reason": {
      "type": "string",
      "enum": [
        "missing-input",
        "target-ambiguous",
        "state-drift",
        "adapter-unavailable",
        "permission-required",
        "policy-blocked",
        "security-signal",
        "checkpoint-mismatch",
        "verification-ambiguous"
      ]
    },
    "reasonCode": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "pattern": "^[a-z][a-z0-9_.-]*$"
    },
    "context": {
      "default": {
        "requiredFieldNames": [],
        "optionIds": []
      },
      "type": "object",
      "properties": {
        "checkpointId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
        },
        "requiredFieldNames": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-z][a-z0-9_.-]*$"
          }
        },
        "optionIds": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          }
        },
        "candidateCount": {
          "type": "integer",
          "minimum": 0,
          "maximum": 10000
        }
      },
      "additionalProperties": false
    },
    "observedAt": {
      "type": "string"
    }
  },
  "required": [
    "assignmentId",
    "bindingHash",
    "reason",
    "reasonCode",
    "observedAt"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/work-uncertainties/:uncertaintyId/resolve`

- The delegated-principal assertion and `bindingHash` bind the exact uncertainty decision.

Example:

```json
{
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "bindingHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "resolution": {
    "kind": "select-option",
    "optionId": "record-1"
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    },
    "bindingHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    },
    "resolution": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "retry"
            }
          },
          "required": [
            "kind"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "cancel"
            }
          },
          "required": [
            "kind"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "select-option"
            },
            "optionId": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
            }
          },
          "required": [
            "kind",
            "optionId"
          ],
          "additionalProperties": false
        }
      ]
    }
  },
  "required": [
    "principalId",
    "principalAssertion",
    "bindingHash",
    "resolution"
  ]
}
```

</details>

### `POST /v1/work/:workItemId/report`

- `outcome` is `performed`, `refused`, or `failed`. Never report success; Munky verifies.

Example:

```json
{
  "outcome": "performed",
  "reference": "email_8841",
  "artifact": "Reminder sent for invoice 8291",
  "performedAt": "2026-09-02T15:01:12.000Z"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "outcome": {
      "type": "string",
      "enum": [
        "performed",
        "refused",
        "failed"
      ]
    },
    "reference": {
      "type": "string",
      "maxLength": 400
    },
    "artifact": {
      "type": "string",
      "maxLength": 4000
    },
    "detail": {
      "type": "string",
      "maxLength": 4000
    },
    "performedAt": {
      "type": "string"
    }
  },
  "required": [
    "outcome"
  ]
}
```

</details>

### `POST /v1/tenants/:tenantId/corrections`

Example:

```json
{
  "kind": "correct-variant-assignment",
  "reason": "same-job-different-route",
  "principalId": "prn_2f1c…",
  "principalAssertion": "eyJhbGciOiJFZERTQSJ9.example.signature",
  "subject": {
    "familyId": "fam_7c2e…",
    "episodeId": "ep_1a9d…"
  },
  "payload": {
    "variantId": "normal"
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "merge-families",
        "split-family",
        "reassign-episode",
        "create-family-from-episodes",
        "alias-equivalent",
        "must-not-share-family",
        "reject-contract",
        "correct-intent",
        "correct-executor-mapping",
        "correct-verifier-mapping",
        "correct-input-fields",
        "correct-terminal-outcome",
        "correct-variant-assignment"
      ]
    },
    "reason": {
      "type": "string",
      "enum": [
        "different-business-outcome",
        "different-target-records",
        "different-side-effects",
        "same-job-different-names",
        "same-job-different-route",
        "wrong-operation-selected",
        "wrong-verification-selected",
        "wrong-inputs-inferred",
        "evidence-was-a-mixture",
        "provider-changed",
        "policy-changed",
        "operator-judgement"
      ]
    },
    "principalId": {
      "type": "string",
      "minLength": 1
    },
    "note": {
      "type": "string",
      "maxLength": 2000
    },
    "subject": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "payload": {
      "default": {},
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "principalAssertion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32768
    }
  },
  "required": [
    "kind",
    "reason",
    "principalId",
    "subject",
    "principalAssertion"
  ]
}
```

</details>

### `POST /v1/webhooks`

- `eventTypes` are from: `job.understood`, `job.eligible`, `proposal.created`, `proposal.approved`, `proposal.declined`, `proposal.expired`, `trial.started`, `trial.exhausted`, `trial.stopped`, `trial.completed`, `work.issued`, `work.verified`, `work.failed`, `work.requires_human`, `receipt.created`, `correction.applied`, `connection.degraded`.
- Optional `secret` (32+ characters) to bring your own signing secret; otherwise one is generated and returned once.

Example:

```json
{
  "url": "https://example.com/webhooks/attesko",
  "eventTypes": [
    "proposal.created",
    "work.issued",
    "receipt.created"
  ]
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri"
    },
    "eventTypes": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "job.understood",
          "job.eligible",
          "proposal.created",
          "proposal.approved",
          "proposal.declined",
          "proposal.expired",
          "trial.started",
          "trial.exhausted",
          "trial.stopped",
          "trial.completed",
          "work.issued",
          "work.verified",
          "work.failed",
          "work.requires_human",
          "receipt.created",
          "correction.applied",
          "connection.degraded"
        ]
      }
    },
    "secret": {
      "type": "string",
      "minLength": 32
    }
  },
  "required": [
    "url",
    "eventTypes"
  ]
}
```

</details>

### `POST /v1/webhooks/deliveries/:eventId/replay`

- No body fields. The event id in the path must be a UUID.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {}
}
```

</details>

### `POST /v1/edge-runtimes/:runtimeId/frame-escalations`

- For a runtime that runs its own frame tier and met a screen its history cannot answer. The runtime must have declared observation support when it registered; the tenant is the runtime's own and cannot be named in the body.
- `promptVersion` names the frame-understanding instruction to answer under. A prompt itself is never sent: Munky renders its own text for the version and refuses a version it does not hold, so a runtime cannot choose what a provider is asked.
- `evidence` must be the redactor's output. A `redactionVersion` of `raw`, `none`, `unredacted`, or `passthrough` is refused before any provider is called and before any allowance is spent. `frameEncoding` must be `png` — an escalation carries an encoded frame — and `frameBase64` is at most 2,000,000 characters, which keeps the whole request inside the 2 MiB body ceiling every other `/v1` route lives with.
- `escalationReason` is a lower-case slug describing why the local tier could not answer, matching `^[a-z][a-z0-9-]{0,40}$`. It is recorded on the departure notice, so it must never carry a window title, a URL, or anything else read off the screen.
- The answer is always 200 and is either `{ "ok": true, "output": …, "usage": … }` or `{ "ok": false, "reason": …, "detail": …, "retryAfterSeconds"?: … }`. `reason` is one of `unsupported-prompt-version`, `budget-exhausted`, `evidence-refused`, or `provider-unavailable`; only `provider-unavailable` should be retried with the same frame, and `budget-exhausted` resolves when the tenant's window does.
- The per-tenant escalation ceiling is spent when the frame is sent and is never refunded, including when the provider fails: what it bounds is frames leaving a machine, and one that left unanswered still left. A ceiling that cannot be read answers `budget-exhausted` too — see ADR-0043.
- Send `x-attesko-idempotency-key` and one frame is one escalation however many times it is sent. A key that has already been answered is answered again with the same `output` and `usage`, at no further cost and against no further allowance; a resend arriving while the first attempt is still running is refused `provider-unavailable` rather than read twice. Omitting the header does not opt out — Munky keys the frame by `evidence.recordId`, which is what the official runtime builds its header from. See ADR-0051.

Example:

```json
{
  "promptVersion": "frames-to-events-v8",
  "maxOutputTokens": 4096,
  "escalationReason": "insufficient-history",
  "evidence": {
    "recordId": "capture-8f21",
    "capturedAt": "2026-09-03T09:15:00.000Z",
    "width": 1440,
    "height": 900,
    "frameBase64": "iVBORw0KGgo=",
    "frameEncoding": "png",
    "redactionVersion": "redactor-v4",
    "activity": {
      "foregroundApplication": "Practice Manager",
      "windowTitle": null,
      "browserUrl": null,
      "pointerActivity": true,
      "keyboardActivity": false
    }
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "promptVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    },
    "maxOutputTokens": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "escalationReason": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]{0,40}$"
    },
    "evidence": {
      "type": "object",
      "properties": {
        "recordId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "capturedAt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 40
        },
        "width": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        },
        "height": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        },
        "frameBase64": {
          "type": "string",
          "minLength": 1,
          "maxLength": 12000000
        },
        "frameEncoding": {
          "type": "string",
          "enum": [
            "rgba",
            "png"
          ]
        },
        "redactionVersion": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120
        },
        "activity": {
          "type": "object",
          "properties": {
            "foregroundApplication": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "windowTitle": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "browserUrl": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "pointerActivity": {
              "type": "boolean"
            },
            "keyboardActivity": {
              "type": "boolean"
            }
          },
          "required": [
            "foregroundApplication",
            "windowTitle",
            "browserUrl",
            "pointerActivity",
            "keyboardActivity"
          ]
        }
      },
      "required": [
        "recordId",
        "capturedAt",
        "width",
        "height",
        "frameBase64",
        "redactionVersion",
        "activity"
      ]
    }
  },
  "required": [
    "promptVersion",
    "maxOutputTokens",
    "evidence"
  ]
}
```

</details>

### `POST /v1/apps/:appId/accounts/signup`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "email": "person@example.com",
  "password": "example-password-long-enough"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "password": {
      "type": "string",
      "minLength": 10,
      "maxLength": 1024
    }
  },
  "required": [
    "email",
    "password"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/accounts/resend`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "email": "person@example.com"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/accounts/verify`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "email": "person@example.com",
  "code": "123456"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "code": {
      "type": "string",
      "pattern": "^\\d{6}$"
    }
  },
  "required": [
    "email",
    "code"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/accounts/recovery/start`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "email": "person@example.com"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/accounts/recovery/verify`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "email": "person@example.com",
  "code": "123456"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "code": {
      "type": "string",
      "pattern": "^\\d{6}$"
    }
  },
  "required": [
    "email",
    "code"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/sessions/sign-in`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "email": "person@example.com",
  "password": "example-password-long-enough"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "maxLength": 254,
      "format": "email",
      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
    },
    "password": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1024
    }
  },
  "required": [
    "email",
    "password"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/sessions/current`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/sessions/revoke`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/workspaces`

- Workspace kind is set by the configured application; MUNKY OS is personal.

Example:

```json
{
  "displayName": "My work"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "displayName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "displayName"
  ],
  "additionalProperties": false
}
```

</details>

### `PUT /v1/apps/:appId/workspaces/:workspaceId/policy`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "paused": true
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "paused": {
      "type": "boolean"
    }
  },
  "required": [
    "paused"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/workspaces/:workspaceId/devices`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "runtimeExternalId": "installation-1",
  "kind": "customer-edge",
  "platform": "macos",
  "runtimeVersion": "1.0.0",
  "protocolVersion": "attesko.edge.runtime.v1",
  "features": {
    "observations": false,
    "execution": "none",
    "observationAdapters": [],
    "executionAdapters": [],
    "uncertaintyChannels": []
  }
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "runtimeExternalId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
    },
    "kind": {
      "type": "string",
      "enum": [
        "attesko-desktop",
        "customer-edge",
        "server-edge"
      ]
    },
    "platform": {
      "type": "string",
      "enum": [
        "macos",
        "windows",
        "linux",
        "server"
      ]
    },
    "runtimeVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "protocolVersion": {
      "type": "string",
      "const": "attesko.edge.runtime.v1"
    },
    "features": {
      "type": "object",
      "properties": {
        "observations": {
          "type": "boolean"
        },
        "execution": {
          "type": "string",
          "enum": [
            "none",
            "adapter"
          ]
        },
        "observationAdapters": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "native-event",
                  "browser-dom",
                  "os-accessibility",
                  "screen-capture"
                ]
              },
              "sourceSystems": {
                "default": [],
                "maxItems": 100,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-z][a-z0-9_.-]*$"
                }
              }
            },
            "required": [
              "kind"
            ]
          }
        },
        "executionAdapters": {
          "default": [],
          "maxItems": 20,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "native-api",
                  "mcp",
                  "browser-dom",
                  "os-accessibility"
                ]
              },
              "capabilityIds": {
                "default": [],
                "maxItems": 200,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-z][a-z0-9_.-]*$"
                }
              },
              "attendance": {
                "type": "string",
                "enum": [
                  "attended"
                ]
              }
            },
            "required": [
              "kind"
            ]
          }
        },
        "uncertaintyChannels": {
          "default": [],
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "local-notification",
              "sms",
              "email",
              "webhook"
            ]
          }
        }
      },
      "required": [
        "observations",
        "execution"
      ]
    }
  },
  "required": [
    "runtimeExternalId",
    "kind",
    "platform",
    "runtimeVersion",
    "protocolVersion",
    "features"
  ]
}
```

</details>

### `POST /v1/apps/:appId/workspaces/:workspaceId/phone-challenges`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "phone": "+15555550123",
  "consent": true
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "phone": {
      "type": "string",
      "pattern": "^\\+[1-9][0-9]{7,14}$"
    },
    "consent": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "phone",
    "consent"
  ],
  "additionalProperties": false
}
```

</details>

### `POST /v1/apps/:appId/workspaces/:workspaceId/phone-challenges/:challengeId/verify`

- Native client sends no project key. Authenticated operations require x-munky-application-session; credentials never belong in URLs.

Example:

```json
{
  "code": "123456"
}
```

<details><summary>JSON Schema</summary>

```json
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "pattern": "^[0-9]{6}$"
    }
  },
  "required": [
    "code"
  ],
  "additionalProperties": false
}
```

</details>

## Event rejection codes

Per-item codes in a 200 ingest response. The request was well formed; the item was not stored.

- `privacy_rejected`
- `tenant_mismatch`
- `schema_invalid`
- `store_failed`

## Errors

| Code | Status | Meaning |
| --- | --- | --- |
| `application_account_owns_workspace` | 409 | Erase all workspaces owned by this account before deleting the account. |
| `application_credentials_invalid` | 401 | Email, password or verification code was not accepted for this application and environment. |
| `application_device_not_found` | 404 | The device is not visible to this workspace member. |
| `application_executor_admin_required` | 403 | Only a workspace owner or administrator can enroll an execution-capable runtime. |
| `application_invitation_invalid` | 404 | The invitation is expired, revoked, consumed or not addressed to this application account. |
| `application_mail_unavailable` | 503 | The application cannot deliver verification email. |
| `application_member_not_found` | 404 | The requested active workspace member was not found. |
| `application_membership_exists` | 409 | This account already has active membership. |
| `application_rate_limited` | 429 | This account or application has reached an authentication attempt or email limit. Do not retry immediately. |
| `application_session_invalid` | 401 | The application session is missing, expired, revoked or belongs to another application/environment. |
| `application_workspace_admin_required` | 403 | Workspace owner or administrator membership is required. |
| `application_workspace_not_found` | 404 | No active membership in this application workspace. |
| `application_workspace_owner_required` | 403 | Only the workspace owner may perform this operation. |
| `assertion_key_exists` | 409 | A key with that id is already registered for the environment. |
| `assertion_key_not_found` | 404 | No such delegated-principal key. |
| `assertion_key_store_not_configured` | 503 | This deployment has no delegated-principal key store. |
| `assignment_expired` | 409 | The Edge assignment lease has expired. |
| `assignment_not_found` | 404 | The assignment does not exist or is not bound to this runtime, tenant, and work item. |
| `attendance_not_declared` | 422 | An execution adapter did not state the attendance its rung has proved. Every built rung is attended: send `"attendance": "attended"` on each declared execution adapter. |
| `binding_hash_mismatch` | 409 | The supplied binding hash is not the exact current assignment or uncertainty binding. |
| `capability_not_declared` | 422 | An execution adapter names a capability that is absent or withdrawn. |
| `company_work_paused` | 403 | The connected business has paused new device work. Existing reports and independent verification remain available. This restriction is specific to the first-party business deployment. |
| `credential_invalid` | 401 | The key is absent, invalid, revoked, or for another environment. Do not retry unchanged. |
| `credentials_invalid` | 401 | The Product email and password pair did not authenticate. |
| `delivery_not_replayable` | 404 | No project-owned delivery with that event id is eligible for replay. |
| `desktop_gateway_unavailable` | 503 | Native gateway is unconfigured or its server credential is invalid or lacks required scopes. |
| `desktop_origin_forbidden` | 403 | This gateway accepts native HTTP requests without a browser Origin only. |
| `desktop_request_failed` | 502 | The gateway could not obtain a valid API response. |
| `device_credential_invalid` | 401 | The Product installation key is unknown, expired, revoked, or presented by another installation. Enrol the installation again. |
| `docs_not_deployed` | 503 | The deployment was built without its docs directory. |
| `forbidden` | 403 | The caller may not perform this operation on this resource. |
| `internal_error` | 500 | Munky's fault. Retry with the same idempotency key; the incident is logged with the request id. |
| `invalid_assertion_key` | 400 | The public key is not a valid Ed25519 SPKI PEM or the key id is malformed. |
| `invalid_environment` | 400 | The environment is not development, staging, or production. |
| `not_found` | 404 | No such resource in this project and environment. |
| `not_ready` | 503 | The database or required application schema is unavailable. |
| `observation_not_declared` | 403 | This runtime did not declare observation support when it registered. |
| `option_not_offered` | 422 | The resolution selects an option that the uncertainty record did not offer. |
| `payload_too_large` | 413 | The JSON body exceeds the route's limit: 2 MiB for events, 256 KiB elsewhere. |
| `personal_workspace_single_member` | 403 | Personal workspaces cannot invite other members. |
| `principal_assertion_invalid` | 401 | The delegated principal assertion does not verify for this decision, or, on a first-party Product decision route, was not signed under the authenticated installation's own key id. Sign the exact decision again on the machine that holds the key. |
| `product_enrollment_failed` | 500 | Product installation enrolment could not be completed. Retry; nothing was issued. |
| `registration_invalid` | 400 | First-party Product account creation was refused: the address or password did not meet the account rules. |
| `replay_unavailable` | 503 | This deployment cannot replay deliveries. |
| `resolution_conflict` | 409 | The uncertainty was already resolved with a different decision. |
| `runtime_not_deployed` | 503 | The local runtime release artifact is unavailable. |
| `runtime_not_found` | 404 | No such Edge runtime is visible to this caller. |
| `runtime_not_ready` | 409 | The runtime is not online or its latest heartbeat is no longer fresh. |
| `scope_required` | 403 | The key lacks the route's required scope. |
| `sdk_not_deployed` | 503 | The release is missing its SDK artifact; contact the operator. |
| `seat_principal_missing` | 409 | This Product seat has no principal yet, so it can decide nothing. Enrol the installation again. |
| `seat_revoked` | 403 | This Product seat or installation has been revoked. Nothing it presents will be accepted; sign the person out. |
| `source_not_declared` | 403 | The event source is not among this runtime's declared observation sources. |
| `stale_heartbeat` | 409 | The heartbeat predates the runtime's latest accepted health state. |
| `tenant_forbidden` | 403 | The tenant is outside this project or environment, or has been erased. Treat as an authorization fault. |
| `text_decision_conflict` | 409 | The request ID, conversation, binding or delivery state conflicts with stored state. Do not replace the prompt or blindly resend. |
| `text_decision_invalid` | 422 | The prompt, principal, tenant, quote or time window is invalid. |
| `text_decision_not_found` | 404 | No decision exists in this tenant and project environment. |
| `text_delivery_unavailable` | 503 | Verified text delivery is not enabled or its provider is not configured. |
| `timestamp_invalid` | 422 | The runtime timestamp is invalid or outside the accepted clock window. |
| `too_many_requests` | 429 | Wait for the Retry-After duration before retrying. |
| `uncertainty_conflict` | 409 | This assignment already has a different open uncertainty record. |
| `uncertainty_expired` | 409 | The uncertainty decision window has expired. |
| `validation_failed` | 422 | The request itself is malformed: not JSON, wrong shape, or a parameter outside its documented form. `detail` lists the fields. Correct it before retrying. |
| `verification_invalid` | 401 | The emailed Product verification code is wrong, already used, or expired. |
| `webhook_delete_unavailable` | 503 | This deployment cannot remove webhooks. |
| `work_lease_missing` | 409 | The core could not bind an Edge assignment because the work lease had no expiry. |
