Skip to main content

Webhook Setup

Webhooks send HTTP POST requests to your server when events occur in SendSeven -- a message is received, a conversation is closed, a contact is created, and more. This enables real-time integrations without polling the API.

Required Scopes

ScopePurpose
webhooks:readList and view webhooks
webhooks:createCreate new webhook endpoints
webhooks:updateUpdate webhook configuration
webhooks:deleteDelete webhooks

How Webhooks Work

  1. You register a webhook endpoint URL and select events to subscribe to
  2. When a subscribed event occurs, SendSeven sends an HTTP POST to your URL
  3. Your server processes the event and returns a 2xx status code to acknowledge receipt
  4. If delivery fails, SendSeven retries with exponential backoff

Endpoint Requirements

Your webhook endpoint must:

  • Use HTTPS (HTTP endpoints are not accepted)
  • Return a 2xx status code within 30 seconds
  • Accept POST requests with a JSON body
  • Be publicly accessible from the internet

Create a Webhook

POST /api/v1/webhook-endpoints

Request Body

FieldTypeRequiredDescription
namestringYesUser-friendly name for the webhook (1-255 chars)
urlstringYesHTTPS endpoint URL
subscribed_eventsarrayYesArray of event types to subscribe to
authorization_headerstringNoAuthorization header value (e.g., Bearer token123)
retry_strategystringNoexponential (default), linear, or none
max_retriesintegerNoMax retry attempts (default: 8, max: 15)
timeout_secondsintegerNoRequest timeout in seconds (default: 30, 5-60)
source_filter_modestringNoall (default) receives events from every channel; selected restricts them to the channels/integrations below
filtered_channel_idsarrayNoChannel IDs to receive events from (only used when source_filter_mode is selected)
filtered_email_integration_idsarrayNoEmail integration IDs to receive events from (only used when source_filter_mode is selected)

curl

curl -X POST "https://api.sendseven.com/api/v1/webhook-endpoints" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"name": "CRM Integration",
"url": "https://yourapp.com/webhooks/sendseven",
"subscribed_events": [
"message.received",
"message.sent",
"conversation.created",
"conversation.closed",
"contact.created"
]
}'

Response (201 Created)

Creation returns the signing secret, not the full endpoint object. Note the field name: it is webhook_id, not id.

{
"webhook_id": "wh_abc123",
"secret_key": "whsec_9f2c4b8e1d6a7053ef4c1b9a8d2e6f30",
"message": "Store this secret key securely. It will not be shown again."
}
Save secret_key now

This is the only time the secret is ever returned. You need it to verify webhook signatures, and it cannot be retrieved later -- only replaced via POST /api/v1/webhook-endpoints/{webhook_id}/regenerate-secret, which invalidates the old one.

Use the returned webhook_id with GET /api/v1/webhook-endpoints/{webhook_id} if you need the full endpoint object (its own id field carries the same value).

List Webhooks

GET /api/v1/webhook-endpoints
curl -X GET "https://api.sendseven.com/api/v1/webhook-endpoints" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"

This endpoint returns a bare JSON array of endpoint objects -- there is no items / pagination envelope, because a tenant's webhook list is small and unpaginated:

[
{
"id": "wh_abc123",
"tenant_id": "tenant_xyz",
"name": "CRM Integration",
"url": "https://yourapp.com/webhooks/sendseven",
"has_authorization_header": false,
"subscribed_events": [
"message.received",
"message.sent",
"conversation.created",
"conversation.closed",
"contact.created"
],
"source_filter_mode": "all",
"filtered_channel_ids": null,
"filtered_email_integration_ids": null,
"is_active": true,
"is_verified": false,
"retry_strategy": "exponential",
"max_retries": 8,
"timeout_seconds": 30,
"last_success_at": "2026-02-10T18:04:11Z",
"last_failure_at": null,
"last_error": null,
"consecutive_failures": 0,
"created_at": "2026-02-10T18:00:00Z",
"updated_at": null
}
]

Update a Webhook

Update a webhook's URL, events, or active status. Only include the fields you want to change.

PATCH /api/v1/webhook-endpoints/{webhook_id}
FieldTypeDescription
namestringUpdated name
urlstringUpdated endpoint URL
subscribed_eventsarrayUpdated event subscriptions (replaces existing list)
is_activebooleanEnable or disable the webhook
authorization_headerstringUpdated auth header (empty string to remove)
curl -X PATCH "https://api.sendseven.com/api/v1/webhook-endpoints/wh_crm_sync" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"subscribed_events": [
"contact.created",
"contact.updated",
"conversation.created",
"conversation.closed",
"message.received"
]
}'
warning

Updating subscribed_events replaces the entire event list. Include all events you want to subscribe to, not just the new ones.

Delete a Webhook

DELETE /api/v1/webhook-endpoints/{webhook_id}
curl -X DELETE "https://api.sendseven.com/api/v1/webhook-endpoints/wh_analytics" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"

View Delivery History

Check delivery status and response times for a webhook:

GET /api/v1/webhook-endpoints/{webhook_id}/deliveries
ParameterTypeDefaultDescription
pageinteger1Page number
page_sizeinteger50Items per page (1--100)
statusstring--pending, success, failed, retrying, dead_letter, or queued (stored while the endpoint is suspended -- see Circuit breaker)
event_typestring--e.g. message.sent, conversation.created
curl -X GET "https://api.sendseven.com/api/v1/webhook-endpoints/wh_crm_sync/deliveries?page=1&page_size=10" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"

This endpoint's pagination fields are flat (total, page, page_size, has_more) rather than nested under a pagination object:

{
"items": [
{
"id": "del_001",
"tenant_id": "tenant_xyz",
"webhook_endpoint_id": "wh_crm_sync",
"event_type": "contact.created",
"event_id": "evt_5f1c9d",
"attempt_number": 1,
"status": "success",
"response_status_code": 200,
"response_time_ms": 145,
"error_message": null,
"next_retry_at": null,
"created_at": "2026-02-10T17:30:00Z",
"completed_at": "2026-02-10T17:30:00Z",
"can_retry": false
},
{
"id": "del_002",
"tenant_id": "tenant_xyz",
"webhook_endpoint_id": "wh_crm_sync",
"event_type": "message.received",
"event_id": "evt_7a3b2e",
"attempt_number": 3,
"status": "failed",
"response_status_code": 500,
"response_time_ms": 2300,
"error_message": "Internal Server Error",
"next_retry_at": null,
"created_at": "2026-02-10T17:35:00Z",
"completed_at": "2026-02-10T17:35:02Z",
"can_retry": true
}
],
"total": 156,
"page": 1,
"page_size": 10,
"has_more": true
}

Deliveries with can_retry: true (status failed or dead_letter) can be replayed manually -- see the retry endpoint below.

Retry Policy

If your endpoint returns a non-2xx status code or times out, SendSeven retries. With the default exponential strategy, the delay before each retry grows and then caps at 30 minutes:

RetryDelay after the failed attempt
1st retry5 seconds
2nd retry10 seconds
3rd retry30 seconds
4th retry2 minutes
5th retry5 minutes
6th retry10 minutes
7th retry and beyond30 minutes (capped)

Retries continue until max_retries attempts have been made (default 8, configurable 0–15). With linear strategy, retries are spaced a fixed 60 seconds apart; with none, no retries are attempted. After the retry budget is exhausted, the delivery is marked as permanently failed.

Manually retry a delivery

A delivery whose can_retry is true (status failed or dead_letter) can be replayed on demand. This creates a new delivery attempt; the original row is left in place for audit.

POST /api/v1/webhook-endpoints/{webhook_id}/deliveries/{delivery_id}/retry

Required Scope: webhooks:update

curl -X POST "https://api.sendseven.com/api/v1/webhook-endpoints/wh_crm_sync/deliveries/del_002/retry" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"success": true,
"delivery_id": "del_009",
"previous_delivery_id": "del_002",
"message": "Delivery queued for retry"
}

Circuit breaker: suspension, not silent loss

If a webhook accumulates 20 consecutive failures, SendSeven suspends it for a bounded 12-hour window rather than disabling it outright. During suspension:

  • Matching events are not dropped — they are stored as queued deliveries (up to 10,000 per endpoint) and replayed in original order once the endpoint recovers.
  • SendSeven re-verifies your endpoint automatically on an escalating schedule: 1m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h after suspension.
  • You receive email notifications across the lifecycle (suspended, reactivated, permanently disabled).

The endpoint's state is visible on the endpoint object: suspended_at is set while suspended, next_reactivation_at holds the next automatic check, and reactivation_attempts counts checks made so far.

OutcomeWhat happens
A re-verification succeeds (automatic or triggered by you)is_active returns to true, suspended_at clears, and the queued backlog is replayed in created_at order
The final check at +12h still failsThe endpoint is permanently deactivated and the queued deliveries are moved to dead_letter

To recover immediately without waiting for the next scheduled check, fix your endpoint and trigger verification yourself:

curl -X POST "https://api.sendseven.com/api/v1/webhook-endpoints/wh_abc123/verify" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"

A successful verification reactivates the endpoint and starts the replay. If the endpoint was permanently deactivated after the 12-hour window, re-enable it with PATCH .../{webhook_id} and {"is_active": true} — but note that its queued events were already dead-lettered by then.

Error Responses

StatusError CodeDescription
401INVALID_TOKENToken is invalid or expired
403INSUFFICIENT_SCOPEToken lacks required webhooks scope
404RESOURCE_NOT_FOUNDWebhook ID does not exist or belongs to another tenant
422VALIDATION_ERRORInvalid URL or events list

Next Steps