Download OpenAPI specification:
Public API for sending SMS messages and subscribing to inbound-message webhooks on the VoxiSMS platform. Outbound SMS is enqueued and delivered by the VoxiSMS Android app via the device's native SMS capability.
Every request uses HMAC-SHA256 request signing. Include these headers:
customer-id: your VoxiSMS customer IDtimestamp: current Unix timestamp in secondssignature: lowercase hex HMAC-SHA256 signatureBuild the signature from this exact canonical string:
METHOD + "\n" + SIGN_PATH + "\n" + TIMESTAMP + "\n" + CUSTOMER_ID + "\n" + BODY
Compute HMAC-SHA256 over the UTF-8 bytes of this canonical string using your VoxiSMS secret key as the HMAC key, then encode the digest as lowercase hexadecimal.
For POST https://api.voxisms.com/v2/enqueue-message, the signed path is
/enqueue-message, not /v2/enqueue-message. The timestamp must be within 5 minutes of
server time, and BODY must be the exact JSON bytes sent in the request body, with no extra
trailing newline.
Inbound-message webhooks are signed the same way in reverse: each delivery carries an
X-VoxiSMS-Signature header you verify with the subscription's signingSecret.
HMAC-authenticated (customer-id + timestamp + signature). Use your VoxiSMS secret key
only as the HMAC key, and send the resulting digest as a lowercase hexadecimal HMAC-SHA256
string in the signature header. Once accepted, the message is queued for delivery and the
response returns the VoxiSMS message identifier.
| id | string or null Optional client-supplied message identifier, limited to 128 UTF-8 bytes. If omitted, null, or blank, VoxiSMS generates one. |
| message required | string non-empty \S SMS message text. Must contain a non-whitespace character and be at most 1,600 UTF-8 bytes. |
| recipient required | string non-empty \S Destination phone number in E.164 format. Must contain a non-whitespace character and be at most 32 UTF-8 bytes. |
| requestedTime | integer or null <int64> Optional requested delivery time as a Unix timestamp in seconds. |
| source | string or null Optional integration or origin identifier, limited to 128 UTF-8 bytes. |
{- "id": "order-123",
- "message": "Hello from VoxiSMS",
- "recipient": "+15551234567",
- "requestedTime": 1700000000,
- "source": "api"
}{- "messageId": "sample-message-id-12345"
}Sent by webhook-dispatcher to the targetUrl of every active subscription whose eventTypes include inbound_message.received, once per inbound SMS. The body is the flat InboundMessageEvent (same shape and same deterministic eventId as GET /inbound-events, so poll and push dedupe consistently). Verify authenticity by recomputing X-VoxiSMS-Signature. Respond 2xx to acknowledge; deliveries are at-least-once, so dedupe on X-VoxiSMS-Event-Id.
| X-VoxiSMS-Timestamp required | string Example: 1721635200 Unix time in seconds at signing; part of the signed string. Reject deliveries whose timestamp is outside your tolerance window to blunt replay. |
| X-VoxiSMS-Event required | string Example: inbound_message.received Event type wire string; currently always |
| X-VoxiSMS-Event-Id required | string Example: evt_a1b2c3d4 The |
| X-VoxiSMS-Delivery-Id required | string Example: del_9f8e7d6c The |
| content required | string |
| customerId required | string |
| event required | string |
| eventId required | string |
| fromNumber required | string |
| messageId required | string |
| messageType required | string |
| occurredAt required | string |
| toNumber required | string |
| version required | string |
{- "content": "Hello from a customer",
- "customerId": "cust_123",
- "event": "inbound_message.received",
- "eventId": "evt_a1b2c3d4",
- "fromNumber": "+15551234567",
- "messageId": "01HZY...",
- "messageType": "sms",
- "occurredAt": "2026-07-22T09:20:00.000Z",
- "toNumber": "+15557654321",
- "version": "1"
}HMAC-authenticated. Returns a BARE JSON array of the SAME event shape the webhook POSTs, so
Zapier's editor "test trigger" step has real sample data. An empty result is [], not an
error.
[- {
- "content": "string",
- "customerId": "string",
- "event": "string",
- "eventId": "string",
- "fromNumber": "string",
- "messageId": "string",
- "messageType": "string",
- "occurredAt": "string",
- "toNumber": "string",
- "version": "string"
}
]HMAC-authenticated (customer-id + timestamp + signature). A re-fired subscribe for
the same (customerId, targetUrl, eventTypes) tuple is idempotent — the returned
subscriptionId and signingSecret are stable across repeats.
| eventTypes | Array of strings (EventType) Default: ["inbound_message.received"] Items Value: "inbound_message.received" Event types to subscribe to. The only supported value is |
Provider (string) Default: "generic" | |
| targetUrl required | string HTTPS URL that receives signed webhook deliveries, limited to 2,048 UTF-8 bytes. Must target a public host; http, localhost, and private/link-local/unspecified addresses are rejected. |
{- "eventTypes": [
- "inbound_message.received"
], - "provider": "generic",
}{- "createdAt": "2019-08-24T14:15:22Z",
- "eventTypes": [
- "inbound_message.received"
], - "signingSecret": "string",
- "status": "active",
- "subscriptionId": "string",
- "targetUrl": "string"
}HMAC-authenticated; the signature covers the full request path (including the
subscription id), since there is no single canonical DELETE route. Idempotent —
deleting an unknown or already-removed subscription still returns 200.
| subscriptionId required | string Subscription id returned by the subscribe call |
{- "error": "string"
}