Skip to content

Webhooks

Webhooks notify your system of SMS events in real-time. When an event occurs, we send an HTTP POST request to your configured endpoint with JSON data describing the event.

Event TypeDescription
SMS Delivery ReceiptTriggered when the delivery status of an outbound SMS changes (delivered, failed, expired, etc.)
Inbound SMSTriggered when an SMS is received on your dedicated virtual number

To configure webhooks, navigate to Settings → Webhooks in your workspace and add your endpoint URL. You can:

  • Enable specific event types for each endpoint
  • Add an optional secret key for HMAC signature validation
  • Add a description to help identify the endpoint

All webhooks are sent as HTTP POST requests with a JSON payload wrapped in a standard envelope.

JSON Data

id
required
string
Unique identifier for this webhook event.
timestamp
required
string
format: date-time (ISO 8601)
When the event occurred.
workspaceId
required
string
The workspace ID associated with this event.
eventType
required
integer
The type of event: 1 for SMS Delivery Receipt, 2 for Inbound SMS.
data
required
object
The event-specific payload (see below).
{
"id": "evt_abc123",
"timestamp": "2025-01-15T10:30:00Z",
"workspaceId": "ws_xyz789",
"eventType": 1,
"data": {
// Event-specific payload
}
}

Sent when the delivery status of an outbound SMS changes. Use this to track whether messages were successfully delivered to recipients.

data Object

messageId
required
string
The unique identifier of the SMS message.
clientReference
string
Your custom reference if provided when sending the SMS.
deliveryStatus
required
string
The current delivery status of the message.
errorCode
string
Error code if the message failed (see error codes below).
processedAt
string
format: date-time (ISO 8601)
When the message was processed by the carrier.
deliveredAt
string
format: date-time (ISO 8601)
When the message was delivered to the recipient.
{
"id": "evt_dr_123456",
"timestamp": "2025-01-15T10:30:00Z",
"workspaceId": "ws_xyz789",
"eventType": 1,
"data": {
"messageId": "12345678",
"clientReference": "order-confirmation-456",
"deliveryStatus": "Delivered",
"errorCode": null,
"processedAt": "2025-01-15T10:29:55Z",
"deliveredAt": "2025-01-15T10:30:00Z"
}
}
StatusFinalDescription
QueuedNoMessage is queued for sending
DispatchedNoMessage has been sent to the carrier
DeliveredYesMessage was successfully delivered to the recipient
FailedYesMessage delivery failed permanently
ExpiredYesMessage expired before delivery (carrier timeout)
RejectedYesMessage was rejected by the carrier
CancelledYesMessage was cancelled before delivery
DeletedYesMessage was deleted
UnknownNoStatus could not be determined

When deliveryStatus is Failed or Rejected, the errorCode field provides additional details.

CodeNameDescription
400QueuedMessage is still queued
401DispatchedMessage is dispatched
402MessageUnroutableNo route available for this destination
403InternalErrorAn internal error occurred
404TemporaryDeliveryFailureTemporary delivery failure - may retry
405UnmatchedParameterInvalid parameter in request
406InternalExpiryMessage expired internally
407CancelledMessage was cancelled
408InternalRejectMessage rejected internally
410UnmatchedDefaultOriginatorSender ID not valid for destination
411ExceededPartsLimitMessage exceeded maximum parts limit
412UnprovisionedRegionDestination region not supported
413BlockedMessage blocked by carrier or filter

Sent when an SMS is received on your dedicated virtual number. This allows you to receive replies and implement two-way SMS conversations.

data Object

messageId
required
string
Unique identifier for the inbound message.
inboundNumber
required
string
format: E.164
Your virtual number that received the message.
sender
required
string
format: E.164
The phone number that sent the message.
body
required
string
The content of the received message.
receivedAt
required
string
format: date-time (ISO 8601)
When the message was received.
{
"id": "evt_in_789012",
"timestamp": "2025-01-15T14:22:30Z",
"workspaceId": "ws_xyz789",
"eventType": 2,
"data": {
"messageId": "inb_987654",
"inboundNumber": "+447700900100",
"sender": "+447700900123",
"body": "Yes, please confirm my appointment",
"receivedAt": "2025-01-15T14:22:30Z"
}
}

If you configure a secret key for your webhook endpoint, we include an HMAC-SHA256 signature that you should validate to ensure the request is authentic.

HeaderDescription
X-Webhook-SignatureBase64-encoded HMAC-SHA256 signature
X-Webhook-TimestampUnix timestamp (seconds) when the webhook was sent

The signature is computed as:

HMAC-SHA256(secret, timestamp + "." + payload)

Where:

  • secret is your configured webhook secret
  • timestamp is the value from X-Webhook-Timestamp
  • payload is the raw JSON request body
using System.Security.Cryptography;
using System.Text;
public bool ValidateWebhookSignature(
string payload,
string signature,
string timestamp,
string secret)
{
var signatureData = $"{timestamp}.{payload}";
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signatureData));
var expectedSignature = Convert.ToBase64String(hash);
return signature == expectedSignature;
}

If your endpoint returns a non-2xx response or times out, we will retry delivery with exponential backoff:

AttemptDelay
1Immediate
25 minutes
315 minutes
41 hour
54 hours
68 hours
712 hours

After 5 consecutive failures, the webhook endpoint is automatically disabled to prevent further failed deliveries. You can re-enable it from the webhook settings page once your endpoint is healthy.


  1. Respond quickly - Return a 2xx response as soon as possible. Process webhook data asynchronously if needed.

  2. Handle duplicates - In rare cases, you may receive the same webhook more than once. Use the id field to deduplicate.

  3. Validate signatures - Always validate the HMAC signature when a secret is configured to ensure requests are authentic.

  4. Use HTTPS - Always use HTTPS endpoints to ensure webhook data is encrypted in transit.

  5. Monitor failures - Check your webhook settings regularly to ensure endpoints haven’t been auto-disabled due to failures.