Callbacks API overview

The Callbacks API defines the outbound HTTP webhook requests dispatched by Ozeki SMS Gateway to your application's callback endpoint URL. Unlike the Messages API and Webhook API (where your application acts as the HTTP client calling the gateway), for callbacks the direction reverses: the gateway acts as the HTTP client and sends HTTP POST requests carrying batches of event envelopes to the HTTP or HTTPS URL exposed by your application.

Table of contents

Target endpoint & direction

All callback deliveries are sent via HTTP POST to the target URL configured by your application:

POST <client_webhook_url>

Headers & authenticity

Every webhook callback HTTP POST request sent by the gateway includes the following headers:

Header Presence Description
Content-Type Always application/json
X-Hub-Signature-256 When an account secret is configured Lowercase hexadecimal HMAC-SHA256 signature of the raw UTF-8 request body, prefixed with sha256= (e.g., sha256=9f83a04b...), computed using the user account's shared webhook secret (whsec_...). Omitted only when dispatching to a per-message webhook.url if no account-level webhook secret is configured.

Data format conventions

  • Always a JSON array: The root request body (WebhookDeliveryPayload) is always formatted as a JSON array of event envelopes (WebhookEventEnvelope[]), never as a standalone JSON object—even when delivering a single event (an array of length 1, up to 100 events per delivery request).
  • Heterogeneous event batches: A single webhook delivery array may contain multiple events of different types (mixing "message.received" and "message.status.updated" in the same HTTP POST request). Receiving endpoints must deserialize the root body as a list/array and inspect the type discriminator on each envelope item.
  • Property naming: JSON field names are strictly case-sensitive and use camelCase.
  • Null omission: Properties with null values inside payload (such as an omitted from sender or unused text/binary fields) are omitted from the serialized JSON output.
  • Timestamps: Both the envelope time and payload timestamps (createdAt, updatedAt) are formatted as ISO 8601 strings with timezone offsets (e.g., "2026-10-21T10:21:00+02:00"). The envelope time indicates when the event actually occurred, not when the HTTP delivery attempt took place.

Callback events summary

Event type (type) Payload model Trigger condition Documentation
"message.received" MessageReceivedPayload Triggered whenever a new inbound SMS message arrives in the user account's inbox folder. webhook_message_received.html
"message.status.updated" MessageStatusUpdatedPayload Triggered whenever an outbound SMS message transitions delivery status (into the sent or notsent folder, or updating its delivery/view state). webhook_message_status_updated.html

Core concepts

Array payload & event envelope (WebhookEventEnvelope)

Every item in the root JSON array conforms to the WebhookEventEnvelope schema, acting as a polymorphic discriminated union where type determines the structure of payload:

Field Type Description
eventId string (UUID) Unique identifier for this webhook event. Preserved across retry attempts; use this value for deduplication and idempotency.
time string (ISO 8601) Timestamp when the underlying event occurred (with timezone offset).
type string Event type discriminator: "message.received" or "message.status.updated".
user string Account username associated with the event.
payload object Event-specific data object: MessageReceivedPayload when type is "message.received", or MessageStatusUpdatedPayload when type is "message.status.updated".

Delivery, batching & retry policy

  • Batching: Up to 100 queued events destined for the same target URL are batched into a single HTTP POST request.
  • Ordering: Events targeting a given destination URL are delivered sequentially in FIFO order. Global delivery order across different destination URLs is not guaranteed.
  • Retries & quadratic backoff: If your callback endpoint does not respond with a 2xx HTTP status code (or if the request times out), the dispatcher retries the delivery up to 20 times using quadratic backoff:

    delay = base + (base × retry2) seconds, where base = 60 seconds.

Signature verification (X-Hub-Signature-256)

When an account-level webhook signing secret (whsec_...) is configured, your receiving server should verify the X-Hub-Signature-256 header before processing the event batch:

  1. Extract header: Read the X-Hub-Signature-256 HTTP header and verify that it is present and begins with the sha256= prefix.
  2. Read raw body bytes: Capture the raw UTF-8 request body bytes directly from the HTTP input stream. Do not parse the JSON into an object model and re-serialize it beforehand, as whitespace or key-order changes will invalidate the signature.
  3. Compute HMAC-SHA256: Calculate the HMAC-SHA256 hash over the raw request body bytes using the UTF-8 bytes of your shared webhook secret (whsec_...). Format the digest as lowercase hexadecimal prefixed with sha256=.
  4. Constant-time comparison: Compare your computed signature string against the received X-Hub-Signature-256 header value using a constant-time equality function (such as CryptographicOperations.FixedTimeEquals in .NET) to prevent timing attacks.

Idempotency & client response requirements

  • Idempotency / deduplication: Because network failures or timeouts can cause a batch to be retried with the exact same eventId values, your receiving endpoint should record processed eventId UUIDs and skip any duplicate events that have already been handled.
  • Client HTTP response: Your endpoint must return a 2xx HTTP status code rapidly to acknowledge receipt.
Client HTTP status code Gateway dispatcher behavior
2xx (200 OK – 299) Delivery is marked as acknowledged and removed from the dispatch queue. No response body is required (any returned payload is ignored).
3xx, 4xx, 5xx, or Timeout Delivery is treated as failed and scheduled for retry according to the quadratic backoff policy (up to 20 retries).

More information