API reference
Every public textbee API endpoint, generated from the OpenAPI spec. Also available as plain text for LLMs.
The base URL is https://api.textbee.dev. Every request is authenticated with an API key created in the dashboard, sent as the x-api-key header.
All endpoints
| Endpoint | Summary |
|---|---|
GET/api/v1/gateway/stats | Get account totals |
GET/api/v1/gateway/devices | List your devices |
GET/api/v1/gateway/devices/{id} | Get a device |
POST/api/v1/gateway/send-sms | Send an SMS |
POST/api/v1/gateway/send-bulk-sms | Send several SMS in one call |
GET/api/v1/gateway/messages | List messages |
GET/api/v1/gateway/devices/{id}/sms/{smsId} | Get one message |
GET/api/v1/gateway/devices/{id}/sms-batch/{smsBatchId} | Get a send batch |
GET/api/v1/webhooks | List your webhook subscriptions |
POST/api/v1/webhooks | Create a webhook subscription |
GET/api/v1/webhooks/notifications | List webhook delivery attempts |
GET/api/v1/webhooks/{webhookId} | Get a webhook subscription |
PATCH/api/v1/webhooks/{webhookId} | Update a webhook subscription |
DELETE/api/v1/webhooks/{webhookId} | Delete a webhook subscription |
gateway
Send SMS and read message history through the Android devices paired with your account.
GET/api/v1/gateway/stats
Get account totals
Messages sent, messages received, devices, and API keys across your whole account.
Response 200: Account totals.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | GatewayStatsDTO | Account totals. |
data.totalSentSMSCountrequired | number | Messages sent across all your devices. |
data.totalReceivedSMSCountrequired | number | Messages received across all your devices. |
data.totalDeviceCountrequired | number | Devices on your account. |
data.totalApiKeyCountrequired | number | API keys on your account. |
Errors
| Status | Description |
|---|---|
| 401 | Missing, invalid, or revoked API key. |
curl "https://api.textbee.dev/api/v1/gateway/stats" \
-H "x-api-key: YOUR_API_KEY"GET/api/v1/gateway/devices
List your devices
Every phone paired with your account. The push token and hardware serial are never returned.
Response 200: Your devices.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | DeviceDTO[] | Your devices. |
data[]._idrequired | string | Device id. Pass it as deviceId when sending. |
data[].userrequired | string | Owner account id. |
data[].enabledrequired | boolean | Whether the device may send and receive SMS. Sending to a disabled device fails. |
data[].isDefaultrequired | boolean | Whether sends without a deviceId go out from this device. |
data[].brandrequired | string | Device brand. |
data[].manufacturerrequired | string | Device manufacturer. |
data[].modelrequired | string | Device model. |
data[].name | string | Your own label for the device. |
data[].buildIdrequired | string | Android build id. |
data[].osrequired | string | Operating system name. |
data[].osVersionrequired | string | Android release version. |
data[].osApiLevel | number | Android SDK level. |
data[].osVersionSource | string | How osVersion was determined. One of: reported, fingerprint, buildId. |
data[].osBuildFingerprint | string | Android build fingerprint. |
data[].appVersionName | string | textbee app version name. |
data[].appVersionCode | number | textbee app version code. |
data[].sentSMSCountrequired | number | Messages this device has sent. |
data[].receivedSMSCountrequired | number | Messages this device has received. |
data[].heartbeatEnabledrequired | boolean | Whether the device reports heartbeats. |
data[].heartbeatIntervalMinutesrequired | number | Minutes between heartbeats. |
data[].receiveSMSEnabledrequired | boolean | Whether incoming messages are forwarded to textbee. Required for received message history and webhooks. |
data[].smsSendDelaySecondsrequired | number | Seconds the device waits between messages in a batch. |
data[].lastHeartbeat | string | Last heartbeat. A device silent for long is likely offline and sends will queue. |
data[].batteryInfo | BatteryInfoDTO | Battery level at the last heartbeat. |
data[].batteryInfo.percentage | number | Battery level, 0 to 100. |
data[].batteryInfo.isCharging | boolean | Whether the device was charging at the last heartbeat. |
data[].batteryInfo.lastUpdated | string | When this reading was taken. |
data[].networkInfo | NetworkInfoDTO | Connection the device was on at the last heartbeat. |
data[].networkInfo.networkType | string | Connection the device last reported. One of: wifi, cellular, none. |
data[].networkInfo.lastUpdated | string | When this reading was taken. |
data[].appVersionInfo | AppVersionInfoDTO | textbee app version running on the device. |
data[].appVersionInfo.versionName | string | textbee app version name. |
data[].appVersionInfo.versionCode | number | textbee app version code. |
data[].appVersionInfo.lastUpdated | string | When this reading was taken. |
data[].deviceUptimeInfo | DeviceUptimeInfoDTO | How long the device has been up. |
data[].deviceUptimeInfo.uptimeMillis | number | Milliseconds since the device booted. |
data[].deviceUptimeInfo.lastUpdated | string | When this reading was taken. |
data[].memoryInfo | MemoryInfoDTO | Memory reported at the last heartbeat. |
data[].memoryInfo.freeBytes | number | Free memory in bytes. |
data[].memoryInfo.totalBytes | number | Total memory in bytes. |
data[].memoryInfo.maxBytes | number | Maximum memory the app may use, in bytes. |
data[].memoryInfo.lastUpdated | string | When this reading was taken. |
data[].storageInfo | StorageInfoDTO | Storage reported at the last heartbeat. |
data[].storageInfo.availableBytes | number | Free storage in bytes. |
data[].storageInfo.totalBytes | number | Total storage in bytes. |
data[].storageInfo.lastUpdated | string | When this reading was taken. |
data[].systemInfo | DeviceSystemInfoDTO | Timezone and locale of the device. |
data[].systemInfo.timezone | string | Device timezone. |
data[].systemInfo.locale | string | Device locale. |
data[].systemInfo.lastUpdated | string | When this reading was taken. |
data[].simInfo | SimInfoCollectionDTO | SIMs installed in the device. |
data[].simInfo.lastUpdatedrequired | string | When the device last reported its SIM list. |
data[].simInfo.simsrequired | SimInfoDTO[] | SIMs currently installed in the device. |
data[].createdAtrequired | string | When the device was registered. |
data[].updatedAtrequired | string | When the device was last updated. |
Errors
| Status | Description |
|---|---|
| 401 | Missing, invalid, or revoked API key. |
curl "https://api.textbee.dev/api/v1/gateway/devices" \
-H "x-api-key: YOUR_API_KEY"GET/api/v1/gateway/devices/{id}
Get a device
Full state of one device, including its last heartbeat, battery, and SIM list.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | string | Device id, from GET /gateway/devices. |
Response 200: The device.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | DeviceDTO | The device. |
data._idrequired | string | Device id. Pass it as deviceId when sending. |
data.userrequired | string | Owner account id. |
data.enabledrequired | boolean | Whether the device may send and receive SMS. Sending to a disabled device fails. |
data.isDefaultrequired | boolean | Whether sends without a deviceId go out from this device. |
data.brandrequired | string | Device brand. |
data.manufacturerrequired | string | Device manufacturer. |
data.modelrequired | string | Device model. |
data.name | string | Your own label for the device. |
data.buildIdrequired | string | Android build id. |
data.osrequired | string | Operating system name. |
data.osVersionrequired | string | Android release version. |
data.osApiLevel | number | Android SDK level. |
data.osVersionSource | string | How osVersion was determined. One of: reported, fingerprint, buildId. |
data.osBuildFingerprint | string | Android build fingerprint. |
data.appVersionName | string | textbee app version name. |
data.appVersionCode | number | textbee app version code. |
data.sentSMSCountrequired | number | Messages this device has sent. |
data.receivedSMSCountrequired | number | Messages this device has received. |
data.heartbeatEnabledrequired | boolean | Whether the device reports heartbeats. |
data.heartbeatIntervalMinutesrequired | number | Minutes between heartbeats. |
data.receiveSMSEnabledrequired | boolean | Whether incoming messages are forwarded to textbee. Required for received message history and webhooks. |
data.smsSendDelaySecondsrequired | number | Seconds the device waits between messages in a batch. |
data.lastHeartbeat | string | Last heartbeat. A device silent for long is likely offline and sends will queue. |
data.batteryInfo | BatteryInfoDTO | Battery level at the last heartbeat. |
data.batteryInfo.percentage | number | Battery level, 0 to 100. |
data.batteryInfo.isCharging | boolean | Whether the device was charging at the last heartbeat. |
data.batteryInfo.lastUpdated | string | When this reading was taken. |
data.networkInfo | NetworkInfoDTO | Connection the device was on at the last heartbeat. |
data.networkInfo.networkType | string | Connection the device last reported. One of: wifi, cellular, none. |
data.networkInfo.lastUpdated | string | When this reading was taken. |
data.appVersionInfo | AppVersionInfoDTO | textbee app version running on the device. |
data.appVersionInfo.versionName | string | textbee app version name. |
data.appVersionInfo.versionCode | number | textbee app version code. |
data.appVersionInfo.lastUpdated | string | When this reading was taken. |
data.deviceUptimeInfo | DeviceUptimeInfoDTO | How long the device has been up. |
data.deviceUptimeInfo.uptimeMillis | number | Milliseconds since the device booted. |
data.deviceUptimeInfo.lastUpdated | string | When this reading was taken. |
data.memoryInfo | MemoryInfoDTO | Memory reported at the last heartbeat. |
data.memoryInfo.freeBytes | number | Free memory in bytes. |
data.memoryInfo.totalBytes | number | Total memory in bytes. |
data.memoryInfo.maxBytes | number | Maximum memory the app may use, in bytes. |
data.memoryInfo.lastUpdated | string | When this reading was taken. |
data.storageInfo | StorageInfoDTO | Storage reported at the last heartbeat. |
data.storageInfo.availableBytes | number | Free storage in bytes. |
data.storageInfo.totalBytes | number | Total storage in bytes. |
data.storageInfo.lastUpdated | string | When this reading was taken. |
data.systemInfo | DeviceSystemInfoDTO | Timezone and locale of the device. |
data.systemInfo.timezone | string | Device timezone. |
data.systemInfo.locale | string | Device locale. |
data.systemInfo.lastUpdated | string | When this reading was taken. |
data.simInfo | SimInfoCollectionDTO | SIMs installed in the device. |
data.simInfo.lastUpdatedrequired | string | When the device last reported its SIM list. |
data.simInfo.simsrequired | SimInfoDTO[] | SIMs currently installed in the device. |
data.createdAtrequired | string | When the device was registered. |
data.updatedAtrequired | string | When the device was last updated. |
Errors
| Status | Description |
|---|---|
| 400 | The device id is not a valid id. |
| 401 | Missing, invalid, or revoked API key. |
| 404 | No device with that id on your account. |
curl "https://api.textbee.dev/api/v1/gateway/devices/ID" \
-H "x-api-key: YOUR_API_KEY"POST/api/v1/gateway/send-sms
Send an SMS
Sends one message to one or more recipients from a phone on your account. deviceId is optional: without it textbee uses your default device, otherwise the enabled device with the most recent heartbeat. Every recipient counts as one message against your plan.
Request body
| Field | Type | Description |
|---|---|---|
messagerequired | string | Text of the message. Long messages are split by the carrier. |
recipientsrequired | string[] | Phone numbers to send to, in international format. Each recipient is billed as one message. |
simSubscriptionId | number | SIM to send from, as subscriptionId from the device simInfo. Defaults to the device default SIM. |
scheduledAt | string | ISO 8601 time to send the message. Must be in the future. Omit to send now. |
deviceId | string | Device to send from. When omitted, uses your default device, or the enabled device with the most recent heartbeat. |
Response 200: The send was accepted. Use smsBatchId to follow delivery. Acceptance is not delivery: the phone still has to be online.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | SendSMSResultDTO | Outcome of the send. |
data.success | boolean | Whether the batch was accepted. Queued sends only. |
data.message | string | Human readable outcome. Queued sends only. |
data.smsBatchId | string | Batch id. Pass it to GET /gateway/devices/{id}/sms-batch/{smsBatchId} to follow delivery. Queued sends only. |
data.recipientCount | number | Number of recipients in the batch. Queued sends only. |
data.successCount | number | Messages pushed to the device. Returned instead of the queue fields when the instance dispatches immediately. |
data.failureCount | number | Messages that could not be pushed to the device. |
Errors
| Status | Description |
|---|---|
| 400 | No enabled device to send from, the device is disabled, your email is not verified, or the message could not be pushed to the phone. |
| 401 | Missing, invalid, or revoked API key. |
| 429 | Your daily, monthly, or per-batch plan limit is used up. The body says which one. |
curl -X POST "https://api.textbee.dev/api/v1/gateway/send-sms" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Your appointment is confirmed for Tuesday at 10am.",
"recipients": [
"+12025550123",
"+12025550124"
]
}'POST/api/v1/gateway/send-bulk-sms
Send several SMS in one call
Sends a batch where every entry has its own text and recipients. deviceId is optional and resolves the same way as POST /gateway/send-sms.
Request body
| Field | Type | Description |
|---|---|---|
messageTemplate | string | Optional label for the batch. Each entry in messages carries its own text. |
messagesrequired | SMSData[] | Messages to send. Every message can target different recipients and use a different SIM. |
messages[].messagerequired | string | Text of the message. Long messages are split by the carrier. |
messages[].recipientsrequired | string[] | Phone numbers to send to, in international format. Each recipient is billed as one message. |
messages[].simSubscriptionId | number | SIM to send from, as subscriptionId from the device simInfo. Defaults to the device default SIM. |
messages[].scheduledAt | string | ISO 8601 time to send the message. Must be in the future. Omit to send now. |
deviceId | string | Device to send from. When omitted, uses your default device, or the enabled device with the most recent heartbeat. |
Response 200: The batch was accepted.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | SendSMSResultDTO | Outcome of the send. |
data.success | boolean | Whether the batch was accepted. Queued sends only. |
data.message | string | Human readable outcome. Queued sends only. |
data.smsBatchId | string | Batch id. Pass it to GET /gateway/devices/{id}/sms-batch/{smsBatchId} to follow delivery. Queued sends only. |
data.recipientCount | number | Number of recipients in the batch. Queued sends only. |
data.successCount | number | Messages pushed to the device. Returned instead of the queue fields when the instance dispatches immediately. |
data.failureCount | number | Messages that could not be pushed to the device. |
Errors
| Status | Description |
|---|---|
| 400 | No enabled device to send from, the device is disabled, your email is not verified, or the batch could not be pushed to the phone. |
| 401 | Missing, invalid, or revoked API key. |
| 429 | Your daily, monthly, or per-batch plan limit is used up. The body says which one. |
curl -X POST "https://api.textbee.dev/api/v1/gateway/send-bulk-sms" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"message": "Your appointment is confirmed for Tuesday at 10am.",
"recipients": [
"+12025550123",
"+12025550124"
],
"simSubscriptionId": 1,
"scheduledAt": "2024-01-15T10:30:00Z"
}
]
}'GET/api/v1/gateway/messages
List messages
Sent and received messages across your whole account, newest first, with delivery status on each. Filter by device, direction, status, text, and time range. Two pagination modes: page numbers for browsing, or cursor for polling. To poll for new messages: request order=asc with a from timestamp, follow nextCursor until hasMore is false, then resume from the last nextCursor on the next poll. Time filters apply to createdAt (when the platform stored the message); for received messages this is upload time, which can lag the receivedAt shown on the message if the device was offline.
Query parameters
| Field | Type | Description |
|---|---|---|
cursor | string | Opaque position from a previous response nextCursor. Returns the page after that position and switches meta to nextCursor/hasMore without a total count. |
limit | integer | Messages per page. Default 50, maximum 100. Defaults to 50. Between 1 and 100. |
page | integer | Page to return. Default 1. Mutually exclusive with cursor. Defaults to 1. |
order | string | desc (default) for newest first; asc to walk forward in time when polling. One of: desc, asc. |
to | string | Exclusive upper bound on createdAt, same formats as from. Exclusive so consecutive windows never double-count a boundary message. |
from | string | Inclusive lower bound on createdAt. ISO-8601 with an explicit timezone (2026-08-01T00:00:00Z or +03:00 form), or a date (2026-08-01, read as UTC midnight). A datetime without a timezone is rejected. |
search | string | Match against the message text and the other party number. Encrypted messages cannot be searched. |
status | string | Delivery state to return. Combine with direction: direction=sent&status=failed lists sends that failed. One of: pending, dispatched, sent, delivered, failed, unknown, received. |
smsBatchId | string | Only messages from this batch, using the smsBatchId returned by a send. Combine with status=failed to list the recipients of a batch that failed. |
direction | string | Direction to return. Default all. Matches the lowercase direction field on each message. Not the same as status: direction=sent means outbound, status=sent means the device dispatched it. One of: all, sent, received. |
deviceIds | string | Comma-separated device ids to include, from GET /gateway/devices. Default: all devices on the account. Messages from deleted devices are never included. |
Response 200: A page of messages.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | MessageDTO[] | Messages, newest first unless order=asc. |
data[]._idrequired | string | Message id. |
data[].message | string | Message text. Empty when the message is end to end encrypted. |
data[].devicerequired | MessageDeviceDTO | Device that sent or received the message. |
data[].device._idrequired | string | Device id. |
data[].device.enabledrequired | boolean | Whether the device may send and receive SMS. |
data[].device.brandrequired | string | Device brand. |
data[].device.modelrequired | string | Device model. |
data[].device.buildIdrequired | string | Android build id. |
data[].status | string | Delivery state, lowercase. Incoming messages are always received. Outgoing messages move from pending to sent, then to delivered when the carrier confirms. Absent on messages stored before status tracking, so treat a missing value as unknown. One of: pending, dispatched, sent, delivered, failed, unknown, received. |
data[].sender | string | Sender number. Set on received messages. |
data[].recipient | string | Destination number. Set on sent messages. |
data[].smsBatch | string | Id of the batch this message was sent in. |
data[].encrypted | boolean | Whether the body is end to end encrypted. Encrypted bodies can only be read by your own client. |
data[].simSubscriptionId | number | SIM the message was sent from. |
data[].receivedAt | string | When the message was received. Received messages only. |
data[].requestedAt | string | When the send was requested. Sent messages only. |
data[].dispatchedAt | string | When the send job reached the device. |
data[].sentAt | string | When the device reported the message as sent. |
data[].deliveredAt | string | When the carrier confirmed delivery. |
data[].failedAt | string | When the message failed. |
data[].errorCode | string | Failure code reported by the device. |
data[].errorMessage | string | Failure reason reported by the device. |
data[].createdAtrequired | string | When the record was created. |
data[].updatedAtrequired | string | When the record was last updated. |
data[].directionrequired | string | Direction of the message, lowercase. Values match the direction query filter, so a response value can be passed straight back as a filter. Not the same thing as status: direction=sent means outbound, status=sent means the device dispatched it. One of: sent, received. |
data[].channel | string | Message channel. Currently always sms; mms will appear here when supported. Absent means sms. One of: sms. |
metarequired | object | Pagination metadata. Page mode (no cursor) returns page, limit, total, totalPages plus nextCursor and hasMore; cursor mode returns limit, nextCursor, and hasMore only, skipping the expensive total count. anyOf rather than oneOf: a page-mode object also satisfies the cursor-mode shape, and oneOf would make strict validators reject it. |
Errors
| Status | Description |
|---|---|
| 400 | Invalid deviceIds, smsBatchId, direction, status, from, to, order, or cursor value. Unknown filter values fail rather than silently applying no filter. |
| 401 | Missing, invalid, or revoked API key. |
| 404 | A deviceIds entry or the smsBatchId is not on your account. |
curl "https://api.textbee.dev/api/v1/gateway/messages?limit=50&page=1" \
-H "x-api-key: YOUR_API_KEY"GET/api/v1/gateway/devices/{id}/sms/{smsId}
Get one message
A single sent or received message with its delivery timestamps. Use it to check the outcome of one recipient.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | string | Device id, from GET /gateway/devices. |
smsIdrequired | string | Message id, from the message history response. |
Response 200: The message.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | RetrieveSMSDTO | The message. |
data._idrequired | string | Message id. |
data.message | string | Message text. Empty when the message is end to end encrypted. |
data.devicerequired | MessageDeviceDTO | Device that sent or received the message. |
data.device._idrequired | string | Device id. |
data.device.enabledrequired | boolean | Whether the device may send and receive SMS. |
data.device.brandrequired | string | Device brand. |
data.device.modelrequired | string | Device model. |
data.device.buildIdrequired | string | Android build id. |
data.status | string | Delivery state, lowercase. Incoming messages are always received. Outgoing messages move from pending to sent, then to delivered when the carrier confirms. Absent on messages stored before status tracking, so treat a missing value as unknown. One of: pending, dispatched, sent, delivered, failed, unknown, received. |
data.sender | string | Sender number. Set on received messages. |
data.recipient | string | Destination number. Set on sent messages. |
data.smsBatch | string | Id of the batch this message was sent in. |
data.encrypted | boolean | Whether the body is end to end encrypted. Encrypted bodies can only be read by your own client. |
data.simSubscriptionId | number | SIM the message was sent from. |
data.receivedAt | string | When the message was received. Received messages only. |
data.requestedAt | string | When the send was requested. Sent messages only. |
data.dispatchedAt | string | When the send job reached the device. |
data.sentAt | string | When the device reported the message as sent. |
data.deliveredAt | string | When the carrier confirmed delivery. |
data.failedAt | string | When the message failed. |
data.errorCode | string | Failure code reported by the device. |
data.errorMessage | string | Failure reason reported by the device. |
data.createdAtrequired | string | When the record was created. |
data.updatedAtrequired | string | When the record was last updated. |
Errors
| Status | Description |
|---|---|
| 400 | The device id is not a valid id. |
| 401 | Missing, invalid, or revoked API key. |
| 404 | No such message on this device. |
curl "https://api.textbee.dev/api/v1/gateway/devices/ID/sms/SMS_ID" \
-H "x-api-key: YOUR_API_KEY"GET/api/v1/gateway/devices/{id}/sms-batch/{smsBatchId}
Get a send batch
The batch returned by a send, plus one message per recipient. Poll this to see how a send is progressing.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | string | Device id, from GET /gateway/devices. |
smsBatchIdrequired | string | Batch id, returned as smsBatchId by the send endpoints. |
Response 200: The batch and its messages.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | SMSBatchResultDTO | The batch and the messages in it. |
data.batchrequired | SMSBatchDTO | The batch itself. |
data.batch._idrequired | string | Batch id. |
data.batch.userrequired | string | Owner account id. |
data.batch.message | string | Message text sent to every recipient in the batch. |
data.batch.encrypted | boolean | Whether the body is end to end encrypted. |
data.batch.recipientCountrequired | number | Recipients in the batch. |
data.batch.recipientPreview | string | Short preview of the recipient list. |
data.batch.successCountrequired | number | Messages sent so far. |
data.batch.failureCountrequired | number | Messages that failed. |
data.batch.status | string | Progress of the batch as a whole, lowercase. Absent on batches stored before status tracking. sent and delivered appear on older batches that mirrored the per-message state. One of: pending, processing, completed, partial_success, failed, unknown, sent, delivered. |
data.batch.error | string | Failure reason when the batch failed. |
data.batch.completedAt | string | When the batch finished. |
data.batch.createdAtrequired | string | When the batch was created. |
data.batch.updatedAtrequired | string | When the batch was last updated. |
data.messagesrequired | RetrieveSMSDTO[] | Every message in the batch, one per recipient. |
data.messages[]._idrequired | string | Message id. |
data.messages[].message | string | Message text. Empty when the message is end to end encrypted. |
data.messages[].devicerequired | MessageDeviceDTO | Device that sent or received the message. |
data.messages[].status | string | Delivery state, lowercase. Incoming messages are always received. Outgoing messages move from pending to sent, then to delivered when the carrier confirms. Absent on messages stored before status tracking, so treat a missing value as unknown. One of: pending, dispatched, sent, delivered, failed, unknown, received. |
data.messages[].sender | string | Sender number. Set on received messages. |
data.messages[].recipient | string | Destination number. Set on sent messages. |
data.messages[].smsBatch | string | Id of the batch this message was sent in. |
data.messages[].encrypted | boolean | Whether the body is end to end encrypted. Encrypted bodies can only be read by your own client. |
data.messages[].simSubscriptionId | number | SIM the message was sent from. |
data.messages[].receivedAt | string | When the message was received. Received messages only. |
data.messages[].requestedAt | string | When the send was requested. Sent messages only. |
data.messages[].dispatchedAt | string | When the send job reached the device. |
data.messages[].sentAt | string | When the device reported the message as sent. |
data.messages[].deliveredAt | string | When the carrier confirmed delivery. |
data.messages[].failedAt | string | When the message failed. |
data.messages[].errorCode | string | Failure code reported by the device. |
data.messages[].errorMessage | string | Failure reason reported by the device. |
data.messages[].createdAtrequired | string | When the record was created. |
data.messages[].updatedAtrequired | string | When the record was last updated. |
Errors
| Status | Description |
|---|---|
| 400 | The device id is not a valid id. |
| 401 | Missing, invalid, or revoked API key. |
| 404 | No such batch on this device. |
curl "https://api.textbee.dev/api/v1/gateway/devices/ID/sms-batch/SMS_BATCH_ID" \
-H "x-api-key: YOUR_API_KEY"webhooks
Subscribe to SMS events and inspect the delivery attempts textbee made for them.
GET/api/v1/webhooks
List your webhook subscriptions
Every subscription on your account, deleted ones excluded. Each one says which events it receives and where they are delivered.
Response 200: Your subscriptions.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | WebhookSubscriptionDTO[] | Your webhook subscriptions. Deleted ones are not returned. |
data[]._idrequired | string | Subscription id. |
data[].userrequired | string | Owner account id. |
data[].name | string | Label shown in the dashboard. |
data[].isActiverequired | boolean | Whether deliveries are attempted. textbee pauses subscriptions that keep failing. |
data[].eventsrequired | string[] | Events this subscription receives. |
data[].deliveryUrlrequired | string | URL events are POSTed to. |
data[].signingSecretrequired | string | Secret used to sign deliveries. |
data[].successfulDeliveryCountrequired | number | Deliveries that succeeded. |
data[].deliveryFailureCountrequired | number | Deliveries that failed. |
data[].deliveryAttemptCountrequired | number | Delivery attempts made, retries included. |
data[].lastDeliveryAttemptAt | string | Last time a delivery was attempted. |
data[].lastDeliverySuccessAt | string | Last time a delivery succeeded. |
data[].lastDeliveryFailureAt | string | Last time a delivery failed. |
data[].notes | WebhookNoteDTO[] | Notes textbee added, such as an auto-pause reason. |
data[].notes[].atrequired | string | When the note was added. |
data[].notes[].textrequired | string | What textbee recorded, for example why the subscription was paused. |
data[].createdAtrequired | string | When the subscription was created. |
data[].updatedAtrequired | string | When it was last updated. |
Errors
| Status | Description |
|---|---|
| 401 | Missing, invalid, or revoked API key. |
curl "https://api.textbee.dev/api/v1/webhooks" \
-H "x-api-key: YOUR_API_KEY"POST/api/v1/webhooks
Create a webhook subscription
textbee POSTs the events you pick to your delivery URL and signs each request with your signing secret, sent as the X-Signature header. Failed deliveries are retried up to 10 times, and a subscription that keeps failing is paused.
Request body
| Field | Type | Description |
|---|---|---|
name | string | Label shown in the dashboard. Up to 64 characters. |
deliveryUrlrequired | string | URL textbee POSTs events to. Must be http or https and publicly reachable. Private and loopback hosts are rejected. |
signingSecretrequired | string | Shared secret, at least 20 characters. textbee signs every delivery with it and sends the signature in the X-Signature header, so your endpoint can verify the request really came from textbee. |
eventsrequired | string[] | Events to deliver. At least one is required. |
Response 201: The subscription was created.
Response 201 fields
| Field | Type | Description |
|---|---|---|
datarequired | WebhookSubscriptionDTO | The subscription. |
data._idrequired | string | Subscription id. |
data.userrequired | string | Owner account id. |
data.name | string | Label shown in the dashboard. |
data.isActiverequired | boolean | Whether deliveries are attempted. textbee pauses subscriptions that keep failing. |
data.eventsrequired | string[] | Events this subscription receives. |
data.deliveryUrlrequired | string | URL events are POSTed to. |
data.signingSecretrequired | string | Secret used to sign deliveries. |
data.successfulDeliveryCountrequired | number | Deliveries that succeeded. |
data.deliveryFailureCountrequired | number | Deliveries that failed. |
data.deliveryAttemptCountrequired | number | Delivery attempts made, retries included. |
data.lastDeliveryAttemptAt | string | Last time a delivery was attempted. |
data.lastDeliverySuccessAt | string | Last time a delivery succeeded. |
data.lastDeliveryFailureAt | string | Last time a delivery failed. |
data.notes | WebhookNoteDTO[] | Notes textbee added, such as an auto-pause reason. |
data.notes[].atrequired | string | When the note was added. |
data.notes[].textrequired | string | What textbee recorded, for example why the subscription was paused. |
data.createdAtrequired | string | When the subscription was created. |
data.updatedAtrequired | string | When it was last updated. |
Errors
| Status | Description |
|---|---|
| 400 | The delivery URL is unusable, the signing secret is shorter than 20 characters, the event list is empty or unknown, or you already have the maximum number of subscriptions. |
| 401 | Missing, invalid, or revoked API key. |
curl -X POST "https://api.textbee.dev/api/v1/webhooks" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"deliveryUrl": "https://example.com/textbee/webhook",
"signingSecret": "SIGNING_SECRET",
"events": [
"MESSAGE_RECEIVED"
]
}'GET/api/v1/webhooks/notifications
List webhook delivery attempts
Delivery history across your subscriptions, newest first. Use it to see what textbee sent, what your endpoint answered, and whether a retry is pending. History is kept for subscriptions you have since deleted.
Query parameters
| Field | Type | Description |
|---|---|---|
page | number | Page to return. Default 1. |
limit | number | Records per page. Default 10. |
status | string | Only return deliveries in this state. One of: pending, retrying, delivered, failed. |
eventType | string | Only return deliveries for this event. One of: MESSAGE_RECEIVED, MESSAGE_SENT, MESSAGE_DELIVERED, MESSAGE_FAILED, UNKNOWN_STATE, SMS_STATUS_UPDATED. |
deviceId | string | Only return deliveries for messages from this device. |
start | string | Start of the time range, ISO 8601. Applied only together with end. |
end | string | End of the time range, ISO 8601. |
webhookSubscriptionId | string | Only return deliveries for one subscription. |
Response 200: A page of delivery records.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | WebhookNotificationPageDTO | Delivery records with pagination. |
data.datarequired | WebhookNotificationDTO[] | Delivery records, newest first. |
data.data[]._idrequired | string | Delivery record id. |
data.data[].webhookSubscriptionrequired | string | Subscription this belongs to. |
data.data[].eventrequired | string | Event that was delivered. One of: MESSAGE_RECEIVED, MESSAGE_SENT, MESSAGE_DELIVERED, MESSAGE_FAILED, UNKNOWN_STATE, SMS_STATUS_UPDATED. |
data.data[].payloadrequired | object | Exact JSON body textbee POSTed to your endpoint. |
data.data[].computedStatusrequired | string | Delivery state. Retrying means textbee will try again, failed means it gave up after 10 attempts. One of: pending, retrying, delivered, failed. |
data.data[].deliveryUrl | string | URL the delivery was sent to when it was attempted. |
data.data[].deliveryAttemptCountrequired | number | Attempts made for this event. |
data.data[].deliveredAt | string | When your endpoint accepted the delivery. |
data.data[].lastDeliveryAttemptAt | string | When the last attempt was made. |
data.data[].nextDeliveryAttemptAt | string | When the next retry is due. |
data.data[].deliveryAttemptAbortedAt | string | When textbee stopped retrying. |
data.data[].errorType | string | Whether the failure is worth retrying. One of: retryable, non-retryable. |
data.data[].httpStatusCode | number | Status code your endpoint returned on the last attempt. |
data.data[].responseBody | string | First 1000 characters of your endpoint response. |
data.data[].idempotencyKey | string | Idempotency key sent with the delivery. |
data.data[].createdAtrequired | string | When the event was recorded. |
data.metarequired | PaginationMetaDTO | Pagination info. |
data.meta.pagerequired | integer | Current page number |
data.meta.limitrequired | integer | Number of items per page |
data.meta.totalrequired | integer | Total number of items |
data.meta.totalPagesrequired | integer | Total number of pages |
Errors
| Status | Description |
|---|---|
| 400 | deviceId or webhookSubscriptionId is not a valid id. |
| 401 | Missing, invalid, or revoked API key. |
| 404 | No webhook subscription with that id on your account. |
curl "https://api.textbee.dev/api/v1/webhooks/notifications" \
-H "x-api-key: YOUR_API_KEY"GET/api/v1/webhooks/{webhookId}
Get a webhook subscription
One subscription with its delivery counters.
Path parameters
| Field | Type | Description |
|---|---|---|
webhookIdrequired | string | Subscription id, from GET /webhooks. |
Response 200: The subscription.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | WebhookSubscriptionDTO | The subscription. |
data._idrequired | string | Subscription id. |
data.userrequired | string | Owner account id. |
data.name | string | Label shown in the dashboard. |
data.isActiverequired | boolean | Whether deliveries are attempted. textbee pauses subscriptions that keep failing. |
data.eventsrequired | string[] | Events this subscription receives. |
data.deliveryUrlrequired | string | URL events are POSTed to. |
data.signingSecretrequired | string | Secret used to sign deliveries. |
data.successfulDeliveryCountrequired | number | Deliveries that succeeded. |
data.deliveryFailureCountrequired | number | Deliveries that failed. |
data.deliveryAttemptCountrequired | number | Delivery attempts made, retries included. |
data.lastDeliveryAttemptAt | string | Last time a delivery was attempted. |
data.lastDeliverySuccessAt | string | Last time a delivery succeeded. |
data.lastDeliveryFailureAt | string | Last time a delivery failed. |
data.notes | WebhookNoteDTO[] | Notes textbee added, such as an auto-pause reason. |
data.notes[].atrequired | string | When the note was added. |
data.notes[].textrequired | string | What textbee recorded, for example why the subscription was paused. |
data.createdAtrequired | string | When the subscription was created. |
data.updatedAtrequired | string | When it was last updated. |
Errors
| Status | Description |
|---|---|
| 401 | Missing, invalid, or revoked API key. |
| 404 | No webhook subscription with that id on your account. |
curl "https://api.textbee.dev/api/v1/webhooks/WEBHOOK_ID" \
-H "x-api-key: YOUR_API_KEY"PATCH/api/v1/webhooks/{webhookId}
Update a webhook subscription
Changes only the fields you send. Use isActive to re-enable a subscription textbee paused after repeated failures.
Path parameters
| Field | Type | Description |
|---|---|---|
webhookIdrequired | string | Subscription id, from GET /webhooks. |
Request body
| Field | Type | Description |
|---|---|---|
name | string | Label shown in the dashboard. Up to 64 characters. |
isActive | boolean | Whether deliveries are attempted. Set it to true to re-enable a subscription textbee paused after repeated failures. |
deliveryUrl | string | New delivery URL. Same rules as on create: http or https, no private or loopback hosts. |
signingSecret | string | New signing secret, at least 20 characters. |
events | string[] | Replacement event list. Cannot be empty. |
Response 200: The updated subscription.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | WebhookSubscriptionDTO | The subscription. |
data._idrequired | string | Subscription id. |
data.userrequired | string | Owner account id. |
data.name | string | Label shown in the dashboard. |
data.isActiverequired | boolean | Whether deliveries are attempted. textbee pauses subscriptions that keep failing. |
data.eventsrequired | string[] | Events this subscription receives. |
data.deliveryUrlrequired | string | URL events are POSTed to. |
data.signingSecretrequired | string | Secret used to sign deliveries. |
data.successfulDeliveryCountrequired | number | Deliveries that succeeded. |
data.deliveryFailureCountrequired | number | Deliveries that failed. |
data.deliveryAttemptCountrequired | number | Delivery attempts made, retries included. |
data.lastDeliveryAttemptAt | string | Last time a delivery was attempted. |
data.lastDeliverySuccessAt | string | Last time a delivery succeeded. |
data.lastDeliveryFailureAt | string | Last time a delivery failed. |
data.notes | WebhookNoteDTO[] | Notes textbee added, such as an auto-pause reason. |
data.notes[].atrequired | string | When the note was added. |
data.notes[].textrequired | string | What textbee recorded, for example why the subscription was paused. |
data.createdAtrequired | string | When the subscription was created. |
data.updatedAtrequired | string | When it was last updated. |
Errors
| Status | Description |
|---|---|
| 400 | The delivery URL is unusable, the signing secret is shorter than 20 characters, or the event list is empty or unknown. |
| 401 | Missing, invalid, or revoked API key. |
| 404 | No webhook subscription with that id on your account. |
curl -X PATCH "https://api.textbee.dev/api/v1/webhooks/WEBHOOK_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Order notifications",
"isActive": true,
"deliveryUrl": "https://example.com/textbee/webhook",
"signingSecret": "SIGNING_SECRET",
"events": [
"MESSAGE_RECEIVED",
"MESSAGE_DELIVERED"
]
}'DELETE/api/v1/webhooks/{webhookId}
Delete a webhook subscription
Stops all deliveries for this subscription. Its delivery history stays readable through GET /webhooks/notifications.
Path parameters
| Field | Type | Description |
|---|---|---|
webhookIdrequired | string | Subscription id, from GET /webhooks. |
Response 200: The subscription was deleted.
Response 200 fields
| Field | Type | Description |
|---|---|---|
datarequired | WebhookDeletedResultDTO | Outcome of the delete. |
data.successrequired | boolean | Whether the delete succeeded. |
Errors
| Status | Description |
|---|---|
| 401 | Missing, invalid, or revoked API key. |
| 404 | No webhook subscription with that id on your account. |
curl -X DELETE "https://api.textbee.dev/api/v1/webhooks/WEBHOOK_ID" \
-H "x-api-key: YOUR_API_KEY"