message.received event to your webhook subscriptions. Events are durable,
signed, and replayable.
Subscribe by passing
events: ["message.received"] on POST /api/v1/webhook-subscriptions.
Configuring a webhook
Two options:- Console — your Chert administrator configures a webhook endpoint for your tenant.
- API — create a subscription programmatically with
POST /api/v1/webhook-subscriptions. See Webhook subscriptions.
signing_secret — rotate either without touching the other.
If you cannot expose a public URL during development, use the SSE stream or dev inbox instead.
message.received envelope
Real-time, fires immediately when an inbound iMessage lands.
message.received
Field map
Compat note. Some legacy deliveries also include top-levelfrom,to,text,message_guid, andline_idaliases. New integrations should prefer thedata.*paths because they are richer and consistent across Chert-managed lines.
Receiving images and attachments
Inbound media is handled by the Chert API layer automatically. When a lead sends an image or file, themessage.received webhook carries a media part in data.message.parts[] with an attachment_id, filename, MIME type, and byte size. Fetch the bytes with the same tenant auth you use for other API calls:
data.message.parts[] when reconstructing the message.
Replying
Two working paths. Pick by what you need:$CHAT_ID is data.chat.id from the webhook. $MESSAGE_GUID is message_guid (or data.message.id) and can be used for tapbacks with /messages/{id}/react. Use a normal chat follow-up for replies.
Headers
These headers are sent by Chert to your webhook endpoint during event delivery. Do not sendx-chert-tenant in your webhook receiver — that header is only for requests your application makes to Chert APIs.
Recommended webhook delivery headers
Legacy compatibility headers
Chert also emits these older lowercase headers for existing integrations. New integrations should read the recommendedX-Webhook-* headers above.
Both signature formats compute the same HMAC:
HMAC-SHA256(subscription_secret, "<ts>.<raw_body>").
Verifying the signature
Compute the HMAC over the raw request body (exact bytes as received) and compare with constant-time equality. Do not parse and re-serialize the JSON before verifying.401.
Retry behavior
Failed events may be retried later by Chert. Pull events through
GET /api/v1/events and replay manually after fixing your endpoint.
At-least-once semantics
You may receive the same event more than once during retries or replays. Always dedup onevent_id (also exposed in the x-chert-event-id header).
Replaying missed events
If your endpoint was down, list events withdelivery.status = failed and replay them:
?subscription_id=<subscription_id>.
See Events for the full debugging surface.

