Download OpenAPI specification:
Tenant-aware SMS and RCS messaging API.
Base URL:
https://messaging.pulseplatform.com/v1
All endpoints require a tenant API key supplied in the x-api-key header.
API keys are provided by your account manager.
Pass your API key in every request:
x-api-key: <your-api-key>
All resources are strictly tenant-scoped: you can only read and send messages that belong to your own tenant.
POST /messages accepts 1–100 destinations in a single call and returns 202 Accepted
immediately. Each destination is queued and dispatched asynchronously. Poll
GET /messages/{messageId} or use the Reporting API to track delivery.
Sending a template message (templateType + templateId) requires the template to
already exist — templates are created and managed in the Pulse UI. There is no API to
create, list, or update templates; templateId must reference one provisioned there.
/messages* endpoints: 100 requests/sec, burst 200. Exceeding it returns
429 Too Many Requests (see POST /messages).
/reporting/* endpoints have a separate, lower limit — see the
Reporting section.
Supply an Idempotency-Key header to make submissions safe to retry. The key is scoped
per-destination (key#to), so re-sending the same request returns the original message
IDs without duplicate sends.
Caveat: if the reservation succeeds but the downstream write fails, retrying with the same key returns the original ID but nothing was enqueued. The endpoint returns
500in this case — retry with a fresh key.
Send SMS and RCS messages, and query their delivery status. Messages are visible here immediately after submission and retained for 90 days.
Use these endpoints for real-time status lookups — e.g. checking whether a specific message was delivered. For filtering, aggregation, or historical queries beyond 90 days, use the Reporting endpoints instead.
Submit 1–100 messages in a single request. Each destination is queued and dispatched asynchronously — the response is 202 Accepted, not delivery confirmation.
Supply Idempotency-Key to make the request safe to retry without duplicate sends.
The key is scoped per-destination (key#to).
| Idempotency-Key | string Optional deduplication key. Re-sending with the same key and destinations returns the original message IDs. If omitted, every call creates new messages. |
| channel required | string Value: "SMS" |
| templateType required | string Value: "textplode" Template provider. |
| templateId required | string non-empty ^[A-Za-z0-9._-]+$ Identifier of a template already provisioned in the Pulse UI. Restricted charset (no path separators). |
Numeric sender ID (string) or Alphanumeric sender ID (string) Sender ID shown to the recipient. Caps: 11 alphanumeric chars or 15 numeric. If omitted, the tenant's default SMS sender is used. | |
required | Array of objects (TemplateDestination) [ 1 .. 100 ] items |
| messageId required | string Unique message identifier. Use this to poll status. |
| status required | string Value: "queued" Always |
| createdAt required | string <date-time> |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| message | string |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
{- "channel": "SMS",
- "sender": "AcmeCorp",
- "destinations": [
- {
- "to": "+447700900001"
}, - {
- "to": "+447700900002"
}
], - "content": {
- "body": {
- "type": "TEXT",
- "text": "Your verification code is 123456. Valid for 10 minutes."
}
}
}[- {
- "messageId": "01HVXXXXXXXXXXXXXXXXXXXXXX",
- "status": "queued",
- "createdAt": "2025-06-19T10:00:00.000Z"
}, - {
- "messageId": "01HVYYYYYYYYYYYYYYYYYYYYYY",
- "status": "queued",
- "createdAt": "2025-06-19T10:00:00.001Z"
}
]Returns a paginated list of your messages, newest first, from the operational store (90-day retention).
Use from + to to restrict the time range. Both must be supplied together or
both omitted. Use cursor from the previous response to page forward.
| from | string <date-time> Start of time range (inclusive). Requires |
| to | string <date-time> End of time range (exclusive). Requires |
| limit | integer [ 1 .. 100 ] Default: 50 |
| cursor | string Opaque pagination cursor from a previous response's |
required | Array of objects (MessageSummary) |
| nextCursor | string Opaque pagination cursor. Pass as |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
{- "items": [
- {
- "messageId": "01HVXXXXXXXXXXXXXXXXXXXXXX",
- "status": "delivered",
- "channel": "SMS",
- "sentTo": "+447700900001",
- "sentFrom": "AcmeCorp",
- "createdAt": "2025-06-19T10:00:00.000Z",
- "submittedAt": "2025-06-19T10:00:01.000Z",
- "deliveredAt": "2025-06-19T10:00:03.000Z",
- "failedAt": null,
- "errorCode": null,
- "source": "direct",
- "templateType": null,
- "templateId": null
}
], - "nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI1LTA2LTE5VDEwOjAwOjAwLjAwMFoiLCJtZXNzYWdlSWQiOiIwMUhWWFhYWFhYWFhYWFhYWFhYWFhYWFgifQ"
}Fetch the current status of a single message by ID (tenant-scoped).
| messageId required | string |
| messageId required | string |
| status required | string (MessageStatus) Enum: "queued" "submitted" "delivered" "failed" "rejected" "expired" Lifecycle state of a message:
|
| channel required | string (Channel) Enum: "SMS" "RCS" |
| sentTo required | string (E164) ^\+[1-9]\d{7,14}$ E.164 phone number (e.g. |
| sentFrom required | string Sender ID used for this message |
| createdAt required | string <date-time> |
| submittedAt | string or null <date-time> |
| deliveredAt | string or null <date-time> |
| failedAt | string or null <date-time> |
| errorCode | string or null Enum: "rejected" "undeliverable" "expired" "invalid_request" null Generic, provider-agnostic reason for a terminal failure. Null unless the message failed. |
| source required | string Enum: "direct" "template" Whether the message was sent directly or compiled from a template. |
| templateType | string or null Template provider (e.g. |
| templateId | string or null Template identifier. Null for direct sends. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
{- "messageId": "01HVXXXXXXXXXXXXXXXXXXXXXX",
- "status": "delivered",
- "channel": "SMS",
- "sentTo": "+447700900001",
- "sentFrom": "AcmeCorp",
- "createdAt": "2025-06-19T10:00:00.000Z",
- "submittedAt": "2025-06-19T10:00:01.000Z",
- "deliveredAt": "2025-06-19T10:00:03.000Z",
- "failedAt": null,
- "errorCode": null,
- "source": "direct",
- "templateType": null,
- "templateId": null
}Query message history from the reporting store. The store is dual-tiered: a fast hot tier for recent data, and a complete cold tier for all-time history. The tier is chosen transparently based on your date filters — you always get the same response shape.
Queries that fall entirely within the 24-month window are served from the hot tier.
If no from/to filters are supplied, the hot tier is used (most-recent-first scan).
A query is routed to the cold tier when:
to ≤ 24-month boundary (query is entirely in the past), orfrom < 24-month boundary (query spans the boundary)Reporting has its own usage plan, separate from /messages*: 10 requests/sec,
burst 20. Exceeding it returns 429 Too Many Requests.
Filter and paginate your message history from the reporting store. The hot/cold tier
is chosen automatically based on your from/to filters — see the section header
for routing rules.
Content cap: when
includeContent=true,limitis capped at 25.
| from | string <date-time> Start of time range (inclusive). Requires |
| to | string <date-time> End of time range (exclusive). Requires |
| status | string (MessageStatus) Enum: "queued" "submitted" "delivered" "failed" "rejected" "expired" Lifecycle state of a message:
|
| channel | string (Channel) Enum: "SMS" "RCS" |
| sentFrom | string |
| sentTo | string (E164) ^\+[1-9]\d{7,14}$ Example: sentTo=+447700900000 E.164 phone number (e.g. |
| limit | integer [ 1 .. 100 ] Default: 20 Max results per page. Capped at 25 when |
| cursor | string Opaque keyset cursor from a previous response's |
| includeContent | string Default: "false" Enum: "true" "false" When |
required | Array of objects (ReportMessage) |
| nextCursor | string Opaque keyset cursor. Pass as |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| message | string |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
{- "items": [
- {
- "messageId": "01HVXXXXXXXXXXXXXXXXXXXXXX",
- "channel": "SMS",
- "status": "delivered",
- "sentFrom": "AcmeCorp",
- "sentTo": "+447700900001",
- "createdAt": "2025-06-19T10:00:00.000Z",
- "submittedAt": "2025-06-19T10:00:01.000Z",
- "deliveredAt": "2025-06-19T10:00:03.000Z",
- "failedAt": null,
- "source": "template",
- "templateType": "textplode",
- "templateId": "welcome-sms"
}
], - "nextCursor": "eyJjIjoiMjAyNS0wNi0xOVQxMDowMDowMC4wMDBaIiwibSI6IjAxSFZYWFhYWFhYWFhYWFhYWFhYWFhYWFgifQ"
}Fetch a single message from the reporting store by ID (tenant-scoped).
Looks up the message in the hot tier first. On a miss (message older than 24 months), falls back transparently to the cold tier. The response shape is identical either way.
| messageId required | string |
| includeContent | string Default: "false" Enum: "true" "false" When |
| messageId required | string |
| channel required | string (Channel) Enum: "SMS" "RCS" |
| status required | string (MessageStatus) Enum: "queued" "submitted" "delivered" "failed" "rejected" "expired" Lifecycle state of a message:
|
| sentFrom required | string |
| sentTo required | string |
| createdAt required | string <date-time> |
| submittedAt | string or null <date-time> |
| deliveredAt | string or null <date-time> |
| failedAt | string or null <date-time> |
| errorCode | string or null Enum: "rejected" "undeliverable" "expired" "invalid_request" null Generic, provider-agnostic reason for a terminal failure. Null unless the message failed. |
| source required | string Enum: "direct" "template" Whether the message was sent directly or compiled from a template. |
| templateType | string or null Template provider (e.g. |
| templateId | string or null Template identifier. Null for direct sends. |
| content | string or null Full message body. Only present when |
| idempotencyKey | string or null Idempotency key used at submission. Only present when |
| providerMetadata | object or null Provider-specific metadata object. Only present when |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
| message | string |
| error required | string Stable machine-readable error code (e.g. |
| message | string Human-readable detail. Not always present. |
{- "messageId": "01HVXXXXXXXXXXXXXXXXXXXXXX",
- "channel": "SMS",
- "status": "delivered",
- "sentFrom": "AcmeCorp",
- "sentTo": "+447700900001",
- "createdAt": "2025-06-19T10:00:00.000Z",
- "submittedAt": "2025-06-19T10:00:01.000Z",
- "deliveredAt": "2025-06-19T10:00:03.000Z",
- "failedAt": null,
- "source": "direct",
- "templateType": null,
- "templateId": null
}