Webhooks
Consio can POST JSON events to your HTTPS endpoint when messages, MMS attachments, calls, or order attributions change. You manage endpoints over the Public API; deliveries are signed so you can verify authenticity.
Create body
{
"url": "https://example.com/webhooks/consio",
"description": "Production SMS and MMS events",
"events": [
"message.received",
"message.delivered",
"message.failed",
"message_media.processed"
]
}Constraints:
- URL must be absolute HTTPS
- Max 20 endpoints per workspace
- Create response includes
secret(whsec_…) once; later responses expose onlysecret_suffix
Events
Only enabled endpoints subscribed to the event receive the delivery.
| Event | When it fires |
|---|---|
message.received | An inbound SMS is persisted |
message.delivered | An outbound SMS is marked delivered |
message.failed | An outbound SMS fails or is rejected at send time |
message_media.processed | An inbound MMS attachment is stored |
call.completed | A call reaches a terminal status |
call.recorded | A call has been recorded and its recording is available |
call.logged | A call is logged with a disposition |
call.summarized | Consio finishes generating the summary of a call |
order.attributed | An order is attributed to a call or an SMS |
Payload
Every delivery carries the same envelope — event_id, event, workspace_rid — plus one object named after the resource the event describes.
Identifiers are opaque resource IDs (rid), never numeric database IDs.
| Event | Payload object |
|---|---|
message.received, message.delivered, message.failed | message |
message_media.processed | media |
call.completed, call.logged | call |
call.recorded | call_recording |
call.summarized | call_summary |
order.attributed | order_attribution |
Message events
message includes a medias array of attachments that were already stored when the event was queued. Inbound MMS attachments are stored after message.received, so that event usually has "medias": []. Subscribe to message_media.processed for each inbound attachment.
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"event": "message.received",
"workspace_rid": "…",
"message": {
"rid": "…",
"type": "SMS",
"content": "Hello",
"trigger_type": "INBOUND",
"customer_rid": "…",
"phone_number_rid": "…",
"user_rid": null,
"ai_agent_rid": null,
"customer_phone_number": "+15551234567",
"external_status": null,
"sent_at": null,
"delivered_at": null,
"failed_at": null,
"created_at": "2026-07-24T12:00:00Z",
"updated_at": "2026-07-24T12:00:00Z",
"medias": []
}
}Message media events
Each inbound attachment fires its own message_media.processed event after Consio stores the file. media.message_rid is the parent SMS. media.url is a signed download URL that expires after 60 minutes (url_valid_until). Fetch the message from the Public API to get a fresh URL after expiry.
{
"event_id": "3e4f5061-7283-49a4-b5c6-d7e8f90a1b2c",
"event": "message_media.processed",
"workspace_rid": "…",
"media": {
"rid": "…",
"message_rid": "…",
"content_type": "image/png",
"size_in_kb": 42.0,
"extension": "png",
"url": "https://storage.googleapis.com/…",
"url_valid_until": "2026-07-24T13:00:00Z"
}
}Call events
call.completed and call.logged carry the same call object.
{
"event_id": "6f1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8",
"event": "call.completed",
"workspace_rid": "…",
"call": {
"rid": "…",
"status": "completed",
"trigger_type": "inbound",
"customer_rid": "…",
"phone_number_rid": "…",
"user_rid": null,
"ai_agent_rid": "…",
"campaign_rid": null,
"customer_phone_number": "+15551234567",
"duration": 128,
"disposition": null,
"notes": null,
"logged_at": null,
"created_at": "2026-07-24T12:00:00Z",
"updated_at": "2026-07-24T12:02:08Z"
}
}On call.logged, disposition and logged_at are set — logged_at is when the disposition was submitted:
{
"event_id": "9c8b7a65-4321-4fed-9876-0123456789ab",
"event": "call.logged",
"workspace_rid": "…",
"call": {
"rid": "…",
"status": "completed",
"trigger_type": "outbound_click",
"customer_rid": "…",
"phone_number_rid": "…",
"user_rid": "…",
"ai_agent_rid": null,
"campaign_rid": null,
"customer_phone_number": "+15551234567",
"duration": 128,
"disposition": "closed_sale",
"notes": "Customer confirmed the reorder",
"logged_at": "2026-07-24T12:04:11Z",
"created_at": "2026-07-24T12:00:00Z",
"updated_at": "2026-07-24T12:04:11Z"
}
}Call summary events
{
"event_id": "1b2c3d4e-5f60-4718-9a2b-3c4d5e6f7081",
"event": "call.summarized",
"workspace_rid": "…",
"call_summary": {
"rid": "…",
"call_rid": "…",
"summary": ["Customer asked about a delayed order"],
"next_steps": ["Send the tracking link"],
"generated_at": "2026-07-24T12:05:00Z"
}
}Call recording events
{
"event_id": "3e4f5061-7283-494a-b5c6-d7e8f90a1b2c",
"event": "call.recorded",
"workspace_rid": "…",
"recording": {
"rid": "…",
"call_rid": "…",
"type": "call",
"duration": 95,
"url": "https://storage.example.com/signed/recording.wav",
"url_valid_until": "2026-07-24T13:00:00Z"
}
}Order attribution events
{
"event_id": "2d3e4f50-6172-4839-a4b5-c6d7e8f90a1b",
"event": "order.attributed",
"workspace_rid": "…",
"order_attribution": {
"rid": "…",
"order_rid": "…",
"call_rid": "…",
"message_rid": null,
"order_total_price": 149.9,
"call_in_progress_at": "2026-07-24T12:00:00Z",
"order_created_at": "2026-07-24T12:30:00Z",
"cancelled_at": null,
"financial_status": "paid",
"created_at": "2026-07-24T12:30:05Z",
"updated_at": "2026-07-24T12:30:05Z"
}
}At least one of call_rid and message_rid is set: it points to the interaction the order is attributed to. call_in_progress_at is when that interaction happened, for a call as well as for an SMS.
event_id is a stable unique identifier. Retries reuse the same event_id — use it for deduplication.
Delivery is at-least-once. There is no ordering guarantee across endpoints or events.
Request format
Each delivery is:
POSTto your URLContent-Type: application/jsonUser-Agent: Consio-Webhooks/1.0X-Consio-Signature: t=<unix_seconds>,v1=<hmac_sha256_hex>- No redirects followed
- 5 second timeout
Verifying signatures
Signed bytes: "{t}." + raw_body (HMAC-SHA256 with your whsec_… secret).
- Read the raw request body before JSON parsing
- Parse
tandv1fromX-Consio-Signature - Reject if
|now - t| > 300seconds (replay protection) - Recompute the HMAC and compare with a constant-time equality check
Retry policy
Consio retries a delivery up to 5 times (6 attempts in total) when your endpoint times out after 5 seconds, when the connection fails, or when it returns 408, 425, 429, or any 5xx. Other 4xx responses are not retried.
For timeouts, connection errors, 408, 425, and 5xx, the wait between attempts follows exponential backoff with jitter: at most 1s, then 2s, 4s, 8s, then 16s (the actual wait is a random value between 0 and that cap). Those retries complete within about a minute.
On 429, Consio ignores that schedule and waits for your Retry-After header instead (default 60 seconds, minimum 1 second, maximum 10 minutes per wait). Five rate-limit retries can therefore stretch the last attempt to about 50 minutes after the first 429. After the fifth retry, Consio stops. Each retry reuses the same event_id.
Updated 18 days ago
