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 Agent-to-Agent.
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
POST /webhooks/subscriptionsCreate 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
GET /webhooks/subscriptionsList 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. |
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
GET /webhooks/subscriptions/{sub_id}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
PATCH /webhooks/subscriptions/{sub_id}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
DELETE /webhooks/subscriptions/{sub_id}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