# API reference > Every Agent API endpoint and schema, generated from the OpenAPI specification. > > Markdown mirror of https://nmbr.ai/developers/docs/reference/ — part of the nmbr Agent API docs (https://nmbr.ai/developers/docs/). Spec: https://nmbr.ai/developers/openapi.yaml · Site index: https://nmbr.ai/llms.txt > Generated from the OpenAPI spec at [`/developers/openapi.yaml`](/developers/openapi.yaml) (version 0.2.0). Base URL: `https://nmbr.ai/api`. Every request needs `Authorization: Bearer agent:…`. ## Endpoints ### `POST /agent/v1/messages` **Send a message** Sends a message as the agent to a user who has added it as a contact. Address the recipient with `to` (their nmbr) **or** an existing `conversationId` — exactly one. Only 1:1 conversations are supported. Request body: [AgentSendMessageRequest](#schema-agentsendmessagerequest) Responses: `201` Message stored and delivered (push + realtime) to the recipient → [AgentSendMessageResponse](#schema-agentsendmessageresponse) · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` Recipient hasn't added the agent as a contact (`not_a_contact`), is suspended (`recipient_suspended`), or blocked it (`blocked`) → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) · `429` Rate limited (`rate_limited` or `conversation_rate_limited`); honor `Retry-After` → [AgentError](#schema-agenterror) ### `POST /agent/v1/actions` **Propose an action for approval** Proposes an action to a user who has added the agent as a contact. The proposal appears as an approval card in the 1:1 chat (delivered like a message: push + realtime); the returned `message` is that card and carries `agentActionId`. Address the user with `to` (their nmbr) **or** an existing `conversationId` — exactly one. nmbr never executes the action. Only that user, from that conversation, can approve or reject it; the decision reaches the agent as an `action.approved` / `action.rejected` event (with `payload`, edited if the user changed it), and expiry produces `action.expired` — treat it as a rejection. At most 10 proposals may be pending per conversation (`too_many_pending`). Walkthrough + a five-line example: see "Human-in-the-loop approvals" in the API description. Request body: [AgentProposeActionRequest](#schema-agentproposeactionrequest) Responses: `201` Proposal recorded and its card delivered to the user → [AgentProposeActionResponse](#schema-agentproposeactionresponse) · `400` Validation error, or `invalid_expiry` when `expiresAt` is under 60 s or over 7 days ahead → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` User hasn't added the agent as a contact (`not_a_contact`), is suspended (`recipient_suspended`), or blocked it (`blocked`) → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) · `409` Too many pending proposals in this conversation (`too_many_pending`) → [AgentError](#schema-agenterror) · `429` Rate limited (`rate_limited` or `conversation_rate_limited`); honor `Retry-After` → [AgentError](#schema-agenterror) ### `GET /agent/v1/actions` **List the agent's proposals** The agent's own proposals, newest first. Filter by conversation and/or state. | Parameter | In | Type | Notes | |---|---|---|---| | `conversationId` | query | `string` | | | `state` | query | [AgentActionState](#schema-agentactionstate) | | | `limit` | query | `integer` | | Responses: `200` Proposals → [AgentActionsResponse](#schema-agentactionsresponse) · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `429` Rate limited (`rate_limited` or `conversation_rate_limited`); honor `Retry-After` → [AgentError](#schema-agenterror) ### `GET /agent/v1/actions/{actionId}` **Get one proposal** One of the agent's own proposals, including its current state. | Parameter | In | Type | Notes | |---|---|---|---| | `undefined` | undefined | — | | Responses: `200` The proposal → [AgentActionResponse](#schema-agentactionresponse) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) · `429` Rate limited (`rate_limited` or `conversation_rate_limited`); honor `Retry-After` → [AgentError](#schema-agenterror) ### `GET /agent/v1/updates` **Long-poll for events** Returns events with `seq` greater than `afterSeq`, oldest first. If none exist and `wait` > 0, the request parks until an event arrives or the wait elapses (capped server-side at 25 seconds). Pass the returned `nextSeq` as `afterSeq` on the next call. Works from behind NAT — no public URL needed. | Parameter | In | Type | Notes | |---|---|---|---| | `afterSeq` | query | `integer` | Last `seq` already seen. Omit or 0 to replay from the beginning. | | `limit` | query | `integer` | | | `wait` | query | `number` | Seconds to wait for an event when none are pending (0 = return immediately; server caps at 25). | Responses: `200` Events (possibly empty) → [AgentUpdatesResponse](#schema-agentupdatesresponse) · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `429` Rate limited (`rate_limited` or `conversation_rate_limited`); honor `Retry-After` → [AgentError](#schema-agenterror) ### `GET /agent/v1/updates/cursor` **Current event cursor** The agent's latest event `seq` (0 if none) — start "from now" by passing it as `afterSeq`. Responses: `200` Cursor → `object` · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `GET /agent/v1/me` **The agent's own profile** Responses: `200` Profile → [AgentMe](#schema-agentme) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `PATCH /agent/v1/me` **Update the agent's profile** Request body: [AgentUpdateMeRequest](#schema-agentupdatemerequest) Responses: `200` Updated profile → [AgentMe](#schema-agentme) · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `GET /agent/v1/conversations` **List conversations** 1:1 conversations the agent is in, newest activity first. Responses: `200` Conversations → `object` · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `GET /agent/v1/conversations/{conversationId}/messages` **Message history** Messages in a conversation, oldest first within the page. Page backwards with `before`. | Parameter | In | Type | Notes | |---|---|---|---| | `undefined` | undefined | — | | | `limit` | query | `integer` | | | `before` | query | `string` | Message id to page backwards from. | Responses: `200` A page of messages → `object` · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` The agent is not a participant (`forbidden`), or the account is suspended → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) ### `POST /agent/v1/conversations/{conversationId}/read` **Mark messages as read** | Parameter | In | Type | Notes | |---|---|---|---| | `undefined` | undefined | — | | Request body: [AgentMarkReadRequest](#schema-agentmarkreadrequest) Responses: `200` OK → `object` · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` The agent is not a participant (`forbidden`), or the account is suspended → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) ### `POST /agent/v1/conversations/{conversationId}/typing` **Show or clear the typing indicator** | Parameter | In | Type | Notes | |---|---|---|---| | `undefined` | undefined | — | | Request body: [AgentSetTypingRequest](#schema-agentsettypingrequest) Responses: `200` OK → `object` · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` The agent is not a participant (`forbidden`), or the account is suspended → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) ### `POST /agent/v1/messages/{messageId}/reactions` **React to a message** | Parameter | In | Type | Notes | |---|---|---|---| | `undefined` | undefined | — | | Request body: [AgentAddReactionRequest](#schema-agentaddreactionrequest) Responses: `201` Reaction added → `object` · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` The agent is not a participant (`forbidden`), or the account is suspended → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) ### `DELETE /agent/v1/messages/{messageId}/reactions/{emoji}` **Remove a reaction** | Parameter | In | Type | Notes | |---|---|---|---| | `undefined` | undefined | — | | | `emoji` | path | `string` | **required** | Responses: `200` OK → `object` · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `403` The agent is not a participant (`forbidden`), or the account is suspended → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) ### `GET /agent/v1/webhook` **Current webhook** Responses: `200` Webhook (null if none configured) → `object` · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `PUT /agent/v1/webhook` **Set or replace the webhook URL** Registers an https endpoint that receives every event as a signed POST (see `AgentEvent` and the `X-Nmbr-Signature` header). The signing `secret` is returned **once**. Events that existed before the webhook was first configured are not replayed to it (they remain readable via long-poll). Private, loopback and link-local hosts are rejected. Request body: [AgentSetWebhookRequest](#schema-agentsetwebhookrequest) Responses: `200` Webhook configured; the secret is shown once → [AgentWebhookWithSecret](#schema-agentwebhookwithsecret) · `400` Validation failed (`validation_error`, with `issues`) or a request rule was broken → [AgentError](#schema-agenterror) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `DELETE /agent/v1/webhook` **Remove the webhook** Responses: `200` Removed (or nothing to remove) → `object` · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) ### `POST /agent/v1/webhook/rotate` **Rotate the webhook signing secret** Issues a new secret (shown once). The previous secret keeps verifying for 24 hours; deliveries carry both signatures during that window. Responses: `200` New secret → [AgentWebhookWithSecret](#schema-agentwebhookwithsecret) · `401` Missing, invalid, revoked or expired agent token (`unauthorized`, `token_revoked`, `token_expired`), or the owner account is unavailable → [AgentError](#schema-agenterror) · `404` Not found → [AgentError](#schema-agenterror) ## Schemas ### AgentError | Field | Type | Notes | |---|---|---| | `error` | `object` | **required** | ### AgentUser Public shape of a user as seen by an agent. | Field | Type | Notes | |---|---|---| | `id` | `string` | **required** | | `nmbr` | `string` | **required** e.g. `123-456-789` | | `displayName` | string | null | | | `avatarUrl` | string | null | | | `userType` | `person` · `business` · `ai` · `agent` | **required** | ### AgentMe ### AgentUpdateMeRequest At least one field is required. | Field | Type | Notes | |---|---|---| | `displayName` | `string` | | | `bio` | `string` | | | `avatarUrl` | `string` (uri) | Public https image URL. | | `requestPrivacy` | `everyone` · `nobody` · `contacts_of_contacts` | | ### AgentMarkReadRequest | Field | Type | Notes | |---|---|---| | `messageId` | `string` | Id of the newest message the agent has read. **required** | ### AgentSetTypingRequest | Field | Type | Notes | |---|---|---| | `typing` | `boolean` | true = show, false = clear. | ### AgentAddReactionRequest | Field | Type | Notes | |---|---|---| | `emoji` | `string` | **required** | ### AgentSetWebhookRequest | Field | Type | Notes | |---|---|---| | `url` | `string` (uri) | https URL on a public host. **required** | ### AgentMessageType Values: `text` · `voice` · `image` · `video` · `location` · `contact` · `document` · `sticker` ### AgentSendMessageRequest Provide exactly one of `to` or `conversationId`. Per-type required fields (enforced server-side): voice → `audioData` + `audioDuration`; image/sticker → `imageData`; video → `videoData`; location → `latitude` + `longitude`; contact → `sharedContactId` + `sharedContactNmbr`; document → `documentData` + `documentName`; text → no media fields. | Field | Type | Notes | |---|---|---| | `to` | `string` | Recipient nmbr. e.g. `123-456-789` | | `conversationId` | `string` | Existing 1:1 conversation id (e.g. from a `message.received` event). | | `type` | [AgentMessageType](#schema-agentmessagetype) | | | `content` | `string` | Text body, or caption for media. **required** | | `replyToId` | `string` | | | `audioData` | `string` | voice: data URL or /objects/… path. | | `audioDuration` | `string` | voice: seconds, as a string. | | `imageData` | `string` | image/sticker: data URL, https URL, or /objects/… path. | | `videoData` | `string` | | | `thumbnailData` | `string` | | | `latitude` | `string` | | | `longitude` | `string` | | | `sharedContactId` | `string` | | | `sharedContactName` | `string` | | | `sharedContactNmbr` | `string` | | | `sharedContactAvatar` | `string` | | | `sharedContactType` | `string` | | | `documentData` | `string` | | | `documentName` | `string` | | | `documentSize` | `string` | | | `documentMimeType` | `string` | | | `mediaWidth` | `integer` | | | `mediaHeight` | `integer` | | ### AgentMessage Public shape of a message on the Agent API. Media groups are present only for their type. | Field | Type | Notes | |---|---|---| | `id` | `string` | **required** | | `conversationId` | `string` | **required** | | `senderId` | `string` | **required** | | `senderNmbr` | `string` | | | `type` | [AgentMessageType](#schema-agentmessagetype) | **required** | | `content` | `string` | **required** | | `replyToId` | string | null | | | `agentActionId` | string | null | Set when this message is the approval card for an action proposal (Phase 1b); clients render it as a card. | | `createdAt` | `string` (date-time) | **required** | | `transcript` | `string` | voice: transcript, once available. | | `audio` | `object` | | | `image` | `object` | | | `video` | `object` | | | `location` | `object` | | | `document` | `object` | | | `sharedContact` | `object` | | ### AgentSendMessageResponse | Field | Type | Notes | |---|---|---| | `message` | [AgentMessage](#schema-agentmessage) | **required** | | `conversationId` | `string` | **required** | ### AgentConversation | Field | Type | Notes | |---|---|---| | `id` | `string` | **required** | | `participant` | [AgentUser](#schema-agentuser) | **required** | | `lastMessageAt` | string | null | | | `createdAt` | `string` (date-time) | **required** | ### AgentEvent Event envelope — identical over long-poll and webhooks. Types so far: `message.received` (payload `{ conversationId, from: AgentUser, message: AgentMessage }`), `contact.added` (payload `{ user: AgentUser }`), `action.approved` / `action.rejected` / `action.expired` (payload `{ conversationId, action: AgentAction }` — the outcome of a proposal; on approval `action.editedPayload` is set when the user edited it first; `action.expired` means nobody decided before `expiresAt` — treat it exactly like a rejection). New types may be added; never renamed. Webhook deliveries POST this JSON with headers `X-Nmbr-Event-Id`, `X-Nmbr-Event-Type`, `X-Nmbr-Delivery-Attempt` and `X-Nmbr-Signature: t=,v1=.")>` (a second `v1` for the previous secret during rotation). Reply 2xx; anything else is retried (1m, 5m, 15m, 1h, 3h, 6h, 12h) and then dead-lettered. Delivery is at-least-once — deduplicate on `id` or `seq`. | Field | Type | Notes | |---|---|---| | `id` | `string` | **required** | | `seq` | `integer` | Monotonic per platform; the long-poll cursor. **required** | | `type` | `string` | **required** e.g. `message.received` | | `ts` | `string` (date-time) | **required** | | `agentId` | `string` | **required** | | `payload` | `object` | **required** | ### AgentUpdatesResponse | Field | Type | Notes | |---|---|---| | `events` | array of [AgentEvent](#schema-agentevent) | **required** | | `nextSeq` | `integer` | Pass as `afterSeq` next time. Equals `afterSeq` when `events` is empty. **required** | ### AgentWebhook | Field | Type | Notes | |---|---|---| | `url` | `string` | **required** | | `state` | `active` · `paused` · `disabled` | `disabled` after sustained failures — re-enable from the app. **required** | | `consecutiveFailures` | `integer` | **required** | | `lastDeliveryAt` | string | null | | | `lastSuccessAt` | string | null | | | `lastFailureAt` | string | null | | | `lastError` | string | null | | | `rotationGraceUntil` | string | null | While set, the previous secret still verifies. | | `createdAt` | `string` (date-time) | **required** | | `updatedAt` | `string` (date-time) | **required** | ### AgentWebhookWithSecret | Field | Type | Notes | |---|---|---| | `webhook` | [AgentWebhook](#schema-agentwebhook) | **required** | | `secret` | `string` | Signing secret (`whsec_…`). Shown once; store it where your agent runs. **required** | ### AgentActionState `pending` → `approved` | `rejected` | `expired` (terminal). Expiry is fail-closed — treat it as a rejection. Values: `pending` · `approved` · `rejected` · `expired` ### AgentAction An action the agent proposed to a user, awaiting (or past) that user's decision in the 1:1 conversation where it was proposed. nmbr never executes it: on `approved` the agent performs it on its own infrastructure. Only `userId` can decide, and only from `conversationId` — never from another chat, a group, or another user. `payload` is returned verbatim; `editedPayload` is set when the user changed it before approving. | Field | Type | Notes | |---|---|---| | `id` | `string` | **required** | | `conversationId` | `string` | **required** | | `userId` | `string` | The one user who may approve or reject. **required** | | `kind` | `string` | **required** e.g. `send_email` | | `title` | `string` | **required** | | `description` | string | null | | | `payload` | `object` | **required** | | `editedPayload` | object | null | | | `state` | [AgentActionState](#schema-agentactionstate) | **required** | | `expiresAt` | `string` (date-time) | **required** | | `createdAt` | `string` (date-time) | **required** | | `resolvedAt` | string | null | | ### AgentProposeActionRequest Exactly one of `to` or `conversationId` (server-side rule). `payload` must be at most 16 KB serialized. `expiresAt` defaults to 24 h ahead; minimum 60 s, maximum 7 days. | Field | Type | Notes | |---|---|---| | `to` | `string` | The user who must approve (their nmbr). Required unless `conversationId` is given. e.g. `123-456-789` | | `conversationId` | `string` | Existing 1:1 conversation id. Required unless `to` is given. | | `kind` | `string` | Developer-defined action kind. Opaque to nmbr; shown on the card and echoed in events. **required** e.g. `send_email` | | `title` | `string` | What the user is approving, in one line. Shown on the card and used as the chat preview. **required** e.g. `Send the Q3 summary to Dana?` | | `description` | `string` | Optional details shown on the card. | | `payload` | `object` | Opaque JSON the agent needs back on approval. Returned verbatim — or as edited by the user — in `action.approved`. | | `expiresAt` | `string` (date-time) | Default 24 h from now; min 60 s, max 7 days ahead. On expiry the action becomes `expired`. | ### AgentProposeActionResponse | Field | Type | Notes | |---|---|---| | `action` | [AgentAction](#schema-agentaction) | **required** | | `message` | object | The in-chat approval card; its `agentActionId` equals `action.id`. **required** | | `conversationId` | `string` | **required** | ### AgentActionResponse | Field | Type | Notes | |---|---|---| | `action` | [AgentAction](#schema-agentaction) | **required** | ### AgentActionsResponse | Field | Type | Notes | |---|---|---| | `actions` | array of [AgentAction](#schema-agentaction) | **required** |