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 and call-lifecycle subscriptions are owned by the agent identity, because that traffic can ride shared lines rather than a number you own — see iMessage webhooks and Phone 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
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 and call-lifecycle 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.* or call.ended). Channels never mix on one row — call.ended cannot share a subscription with imessage.* events. |
context_config | object | null | No | Opt this subscription into per-class conversation context delivered inside received-event payloads. Omit or null for none. |
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. call.ended together with imessage.* 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). |
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 | An active subscription with the same (owner, url) pair already exists. |
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: send an object to replace the stored conversation-context config, null to clear it, or omit the field to leave it unchanged. |
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. |
404 | Subscription does not exist or is not visible to the caller. |
409 | The new (owner, url) collides with another active subscription. |
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 when this is an iMessage or call-lifecycle subscription; 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. |
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 |
An agent identity can own subscriptions on either identity channel, but a single subscription carries one channel — call.ended and imessage.* never share a row.
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 subscription into conversation context. 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: an object replaces the stored config, null clears it, and omitting the field leaves it unchanged.
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