Configure webhook
Registers a new webhook subscription (POST) or creates/replaces the existing webhook subscription (PUT) for the authenticated user account. Each user account supports a single active webhook configuration.
POST /api/v2/webhook PUT /api/v2/webhook
Table of contents
Endpoint summary
| Property | Value |
| HTTP methods | POST, PUT |
| Endpoint path | /api/v2/webhook |
| Authentication | Authorization: Bearer <api_key> or Authorization: Basic <base64(username:password)> |
| Content-Type | application/json |
| Success status codes | 201 Created (POST or newly created via PUT), 200 OK (replaced via PUT) |
POST vs. PUT behavior
| Method | When no webhook exists | When a webhook already exists |
POST /api/v2/webhook |
Creates the webhook subscription, generates a new signing secret, and returns 201 Created. |
Rejects the request with 409 Conflict. |
PUT /api/v2/webhook |
Creates the webhook subscription, generates a new signing secret, and returns 201 Created. |
Replaces the existing webhook subscription, generates a new signing secret, and returns 200 OK. |
Important: The shared signing secret (whsec_...) is returned only in the POST and PUT configuration responses. Store it securely upon registration to verify the X-Hub-Signature-256 HMAC-SHA256 header on incoming webhook deliveries.
Request
Request headers
| Header | Required | Description |
Authorization |
Yes | Credentials identifying the user account context (Bearer <api_key> or Basic <credentials>). |
Content-Type |
Yes | Must be application/json. |
Request body
The request body must be a JSON object with strictly case-sensitive camelCase property names.
Parameters (ConfigureWebhookRequest)
| Field | Type | Required | Description |
url |
string | Yes | Target absolute http:// or https:// endpoint URL where webhook event batches will be delivered via HTTP POST. |
events |
string[] | Yes | Non-empty array of event types to subscribe to ("message.received", "message.status.updated"). |
Supported webhook event types
| Event type | Trigger condition |
"message.received" |
Triggered whenever a new inbound SMS arrives in the user's inbox folder. |
"message.status.updated" |
Triggered whenever an outbound SMS transitions delivery status (into the sent or notsent folder). |
Response
Response headers
| Header | Condition | Description |
Content-Type |
Always | application/json |
Location |
Returned on 201 Created |
Absolute path URI to the user account's webhook resource: /api/v2/webhook. |
201 Created / 200 OK
Returns a WebhookResponse object containing the assigned webhookId, target url, subscribed events, and the newly generated shared signing secret.
| Field | Type | Description |
webhookId |
string (UUID) | Unique internal identifier assigned to the webhook configuration. |
url |
string | Registered target HTTP or HTTPS notification URL. |
events |
string[] | Array of subscribed event types. |
secret |
string | Shared signing secret (prefixed with whsec_) used to compute the X-Hub-Signature-256 HMAC-SHA256 signature on webhook deliveries. |
Error responses
When authentication fails, a webhook is already registered on a POST request, or the request body fails validation, the API returns a structured ApiErrorResponse object.
Common error status codes:
| Status code | Condition | Description |
401 Unauthorized |
Missing or invalid credentials | Authentication credentials were not provided or could not be resolved to an active user. |
409 Conflict |
Webhook already exists (POST only) |
A webhook subscription is already active for the user account. Use PUT /api/v2/webhook to replace it or DELETE /api/v2/webhook to remove it. |
422 Unprocessable Entity |
Validation error | url is missing or not a valid absolute HTTP/HTTPS URL, or events is missing, empty, or contains an unsupported event name. |
Error schema (ApiErrorResponse):
| Field | Type | Description |
error.code |
integer | HTTP status code (e.g., 401, 409, 422). |
error.description |
string | Explanation of the specific error condition. |
error.action |
string | Recommended action to resolve the error. |
error.timestamp |
string (ISO 8601) | Timestamp when the error occurred. |
Examples
1. Register a new webhook (POST)
Registers a new webhook subscribed to both inbound messages and outbound delivery status updates.
Request:
POST /api/v2/webhook HTTP/1.1
Host: localhost:9509
Authorization: Bearer <api_key>
Content-Type: application/json
{
"url": "https://client.example.com/api/sms/inbound",
"events": [
"message.received",
"message.status.updated"
]
}
Response (201 Created):
Response headers:
Location:/api/v2/webhookContent-Type:application/json
{
"webhookId": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
"url": "https://client.example.com/api/sms/inbound",
"events": [
"message.received",
"message.status.updated"
],
"secret": "whsec_9f83a04b12c845d09e3a7b6c5d4e1f2a"
}
2. Replace an existing webhook (PUT)
Replaces the currently active webhook configuration with a new URL or event list and rotates the signing secret.
Request:
PUT /api/v2/webhook HTTP/1.1
Host: localhost:9509
Authorization: Bearer <api_key>
Content-Type: application/json
{
"url": "https://client.example.com/api/sms/inbound",
"events": [
"message.received",
"message.status.updated"
]
}
Response (200 OK):
Response headers:
Content-Type:application/json
{
"webhookId": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
"url": "https://client.example.com/api/sms/inbound",
"events": [
"message.received",
"message.status.updated"
],
"secret": "whsec_8e72b15a23d956e10f4b8c7d6e5f2a3b"
}
3. Subscribe to inbound messages only (POST)
Registers a webhook subscribed exclusively to message.received events.
Request:
POST /api/v2/webhook HTTP/1.1
Host: localhost:9509
Authorization: Bearer <api_key>
Content-Type: application/json
{
"url": "https://client.example.com/api/sms/inbound",
"events": [
"message.received"
]
}
Response (201 Created):
Response headers:
Location:/api/v2/webhookContent-Type:application/json
{
"webhookId": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
"url": "https://client.example.com/api/sms/inbound",
"events": [
"message.received"
],
"secret": "whsec_9f83a04b12c845d09e3a7b6c5d4e1f2a"
}
4. Error: Webhook already exists on POST (409 Conflict)
Returned when attempting to register a webhook via POST when the authenticated user account already has an active subscription.
Response (409 Conflict):
{
"error": {
"code": 409,
"description": "A webhook is already configured for this user account.",
"action": "User 'http_user' already has an active webhook subscription. Use PUT to replace it or DELETE to remove it.",
"timestamp": "2026-10-21T10:21:00+02:00"
}
}