Webhook Subscriptions
A webhook subscription names one owner — a mailbox, a phone number, or an agent identity — one HTTPS destination URL, and a non-empty list of event types from the webhook catalog. When a matching event fires, Inkbox POSTs the payload to the subscription's URL.
Mail and text subscriptions are owned by the mailbox or phone number. iMessage, call-lifecycle, and Agent2Agent subscriptions are owned by the agent identity, which stays the stable owner across channels — see iMessage webhooks, Phone webhooks, and A2A webhooks.
You can attach up to 20 active subscriptions per owner (mailbox, phone number, or agent identity). Each URL receives its own POST per event, independently — one slow receiver doesn't block delivery to the others. A 21st POST /webhooks/subscriptions on the same owner returns 409; delete an existing subscription first.
phone.incoming_callis not subscribable. That event is a synchronous control-plane callback — the response body decides whether Inkbox answers the call — so it can't fan out. Configure it via theincoming_call_webhook_urlfield on the phone number resource instead.
Auth
| Caller | Visibility |
|---|---|
| Admin-scoped API key | Every subscription in the organization. |
| Human session via the Inkbox Console | Every subscription in the organization. |
| Claimed agent-scoped API key | Only subscriptions whose owning mailbox or phone number belongs to that agent's identity, or whose owner is the identity itself. |
Unclaimed agent-scoped API keys are rejected.
Create subscription POST
Create a new subscription on a mailbox, a phone number, or an agent identity.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
mailbox_id | UUID | Conditionally | Owning mailbox. Exactly one of mailbox_id / phone_number_id / agent_identity_id must be set. |
phone_number_id | UUID | Conditionally | Owning phone number. Exactly one of the three owner fields must be set. |
agent_identity_id | UUID | Conditionally | Owning agent identity, for iMessage, call-lifecycle, and Agent2Agent subscriptions. Exactly one of the three owner fields must be set. |
url | string | Yes | HTTPS destination for delivered events. |
event_types | array of strings | Yes | Non-empty, distinct list of event types. Every entry must belong to one channel valid for the owner (mailbox_id → message.*; phone_number_id → text.*; agent_identity_id → imessage.*, call.ended, or a2a.*). Channels never mix on one row. |
context_config | object | null | No | Opt a mail, text, or iMessage subscription into per-class conversation context delivered inside received-event payloads. Agent2Agent subscriptions do not support conversation context; omit this field or send null. |
Request example
Create subscription response
A successful create returns 201 with the created subscription: the shared Subscription object plus a one-time signing_key field that no other endpoint returns.
| Field | Type | Description |
|---|---|---|
signing_key | string | null | The owning identity's webhook signing secret, returned once — only on the first subscription you create for an identity that had no key yet. null (or omitted) on every other create. Store it now; it cannot be retrieved again. See Signing keys. |
Signing keys are per owning identity, so the secret returned here belongs to this subscription's owning identity (owner_identity_id) and verifies every webhook for that identity. All other fields are the shared Subscription object.
Validation errors
| Status | Reason |
|---|---|
422 | Body did not specify exactly one of mailbox_id / phone_number_id / agent_identity_id. |
422 | event_types was empty or contained duplicates. |
422 | An event type doesn't belong to a channel valid for the owner (e.g. text.received on a mailbox_id, or imessage.received on a phone_number_id). |
422 | event_types mixed channels — e.g. a2a.task.created together with imessage.received on one identity-owned row. Create one subscription per channel. |
422 | phone.incoming_call was included — managed via the phone-number resource instead. |
422 | context_config was malformed: an unknown class key, an unknown mode, count outside 1–50, hours outside 1–168, or an unexpected field (the object is validated strictly). |
422 | A non-null context_config was supplied for an Agent2Agent subscription. |
403 | The owner belongs to a different organization than the caller. |
404 | The owner is not visible to the caller (does not exist, or hidden by access scope). |
409 | A mailbox or phone-number subscription already uses the same URL, or an identity subscription at that URL already contains one or more requested event types. Disjoint identity subscriptions may share a URL. |
Code examples
List subscriptions GET
List active subscriptions visible to the caller, newest first. Returns an object with a subscriptions array — not a bare array.
Query parameters
| Parameter | Type | Description |
|---|---|---|
mailbox_id | UUID | Filter by owning mailbox. The three owner filters are mutually exclusive. |
phone_number_id | UUID | Filter by owning phone number. The three owner filters are mutually exclusive. |
agent_identity_id | UUID | Filter by owning agent identity. The three owner filters are mutually exclusive. |
url | string | Filter by exact destination URL. |
event_type | string | Filter by event type. phone.incoming_call is rejected — that event isn't stored as a subscription. |
limit | integer | Maximum rows to return (1–200). Omit to return all visible subscriptions. |
offset | integer | Rows to skip (0–10,000). Defaults to 0. |
Filters AND-combine. Foreign-organization rows are filtered out, so they never appear in results.
Response (200)
Error responses
| Status | Reason |
|---|---|
422 | More than one owner filter supplied. |
422 | event_type=phone.incoming_call. |
Code examples
Get subscription GET
Path parameters
| Parameter | Type | Description |
|---|---|---|
sub_id | UUID | Unique subscription identifier. |
Response (200)
Returns the subscription object. See Subscription object.
Error responses
| Status | Reason |
|---|---|
404 | Subscription does not exist or is not visible to the caller. Foreign-organization subscriptions return 404. |
Code examples
Update subscription PATCH
Update a subscription's destination URL and/or event list in place. The owner FKs are immutable — to move a subscription to a different mailbox, phone number, or agent identity, delete it and create a new one.
Path parameters
| Parameter | Type | Description |
|---|---|---|
sub_id | UUID | Unique subscription identifier. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | No | New HTTPS destination. Omit to leave unchanged. |
event_types | array of strings | No | New non-empty, distinct list of event types. Same channel-coherence rules as create. Omit to leave unchanged. |
context_config | object | null | No | Tri-state for mail, text, and iMessage subscriptions: send an object to replace the stored conversation-context config, null to clear it, or omit the field to leave it unchanged. Agent2Agent subscriptions do not support non-null context configuration. |
Supplying any subset of these is fine. Supplying none no-ops.
Request example
Response (200)
Returns the updated subscription object. See Subscription object.
Validation errors
| Status | Reason |
|---|---|
422 | event_types was empty or contained duplicates. |
422 | An event type doesn't belong to the owner's channel. |
422 | phone.incoming_call was included. |
422 | The replacement event_types mixed channels, or the effective Agent2Agent subscription had a non-null context_config. |
404 | Subscription does not exist or is not visible to the caller. |
409 | The effective event types overlap another active identity subscription using the same URL. Identity subscriptions may share a URL when their event types are disjoint. |
Code examples
Delete subscription DELETE
Remove a subscription. Inkbox stops delivering events to the URL immediately. Subsequent reads on the same sub_id return 404.
Path parameters
| Parameter | Type | Description |
|---|---|---|
sub_id | UUID | Unique subscription identifier. |
Response (204)
No body.
Error responses
| Status | Reason |
|---|---|
404 | Subscription does not exist or is not visible to the caller. |
Code examples
Subscription object
| Field | Type | Description |
|---|---|---|
id | UUID | Unique subscription identifier. |
organization_id | string | Owning organization (e.g. "org_2abc123def456"). Server-derived from the owner — never read from the request body. |
mailbox_id | UUID | null | Owning mailbox. Populated when this is a mail subscription; null otherwise. |
phone_number_id | UUID | null | Owning phone number. Populated when this is a text subscription; null otherwise. |
agent_identity_id | UUID | null | Owning agent identity. Populated for iMessage, call-lifecycle, and Agent2Agent subscriptions; null otherwise. |
owner_identity_id | string | null | The agent identity that owns this subscription, resolved from whichever owner is set (mailbox and phone-number subscriptions report their identity; identity-owned subscriptions report themselves). Normally resolved to the owning identity, but can be null for unresolved or legacy rows. When present, it's the stable way to see which identity a subscription belongs to — and thus which signing key verifies it. |
url | string | HTTPS destination for delivered events. |
event_types | array of strings | Event types this subscription delivers. Every value belongs to the owner's channel. |
context_config | object | null | Per-class conversation-context configuration, echoing what you set, or null when the subscription requests no context. Always null for Agent2Agent subscriptions. |
status | string | Always "active" on returned rows. Deleted rows are not returned by any endpoint. |
created_at | string | ISO 8601 timestamp the subscription was created. |
updated_at | string | ISO 8601 timestamp the subscription was last updated. |
Event types
event_types is a non-empty list drawn from the same catalog the webhook payload pages document:
| Channel | Allowed values |
|---|---|
Mail (mailbox_id owner) | message.received, message.sent, message.forwarded, message.delivered, message.bounced, message.failed |
Phone text (phone_number_id owner) | text.received, text.sent, text.delivered, text.delivery_failed, text.delivery_unconfirmed |
iMessage (agent_identity_id owner) | imessage.received, imessage.reaction_received, imessage.sent, imessage.delivered, imessage.delivery_failed |
Call lifecycle (agent_identity_id owner) | call.ended |
Agent2Agent (agent_identity_id owner) | a2a.task.created, a2a.task.message, a2a.task.canceled, a2a.sent_task.updated |
An agent identity can own subscriptions on any identity channel, but a single subscription carries one channel. Separate identity subscriptions may use the same URL when their event lists are disjoint; requesting the same event on two active rows at that URL returns 409.
phone.incoming_call is not in this list. It is managed via incoming_call_webhook_url on the phone number resource.
Conversation context (context_config)
Set context_config to opt a mail, text, or iMessage subscription into conversation context. Agent2Agent subscriptions do not support this field. On received events — message.received, text.received, and imessage.received — the delivered payload then carries a data.context object with recent history, so your handler can act with context without a follow-up API call. Delivery-status events never carry context.
context_config holds up to three independent class keys:
| Class | What it includes |
|---|---|
email | Recent emails: the triggering thread on mail events, or mail exchanged with the matched contact on text/call events. |
texts | Recent SMS and iMessage, merged into one chronological list. |
calls | Recent calls, each with its transcript. |
Omit a class to leave it unconfigured. Each configured class picks one of two modes:
| Mode | Field | Meaning |
|---|---|---|
count | count (1–50) | The last N items of that class. |
window | hours (1–168) | Everything from the last H hours, capped at the 50 newest items. |
The object is validated strictly: an unknown class key, an unknown mode, a count/hours out of range, or any unexpected field returns 422. An all-empty object is stored as no context (null).
On update, context_config is tri-state for supported channels: an object replaces the stored config, null clears it, and omitting the field leaves it unchanged. A non-null object is rejected when the subscription's effective event family is Agent2Agent.
The delivered data.context block — its scopes, per-class items, transcript abridgment, skip reasons, and the 256 KB size cap — is documented alongside the payloads on the Mail, Phone, and iMessage webhook pages.
- Webhooks guide — payload verification, typed receiver examples
- Mail webhooks reference —
message.*payload shapes - Phone webhooks reference —
text.*,call.ended, andphone.incoming_callpayload shapes - iMessage webhooks reference —
imessage.*payload shapes - Signing keys — per-identity keys used to verify webhook bodies