One API
One endpoint and one API key for every messaging channel, with declarative fallback routing when a channel fails to deliver.
Overview
One API is a single endpoint that sends a message over whichever channel you name, and automatically retries the next channel in a chain you define when the first one fails. It replaces five request shapes, five error conventions and five sets of retry logic with one contract.
It is additive. Every dedicated endpoint under /v1/whatsapp, /v1/sms, /v1/rcs, /v1/email and /v1/instagram keeps working exactly as before, including campaigns, templates and per-channel analytics that only exist there.
Supported channels
Five channels are reachable through the unified endpoint. Voice uses its own endpoint because a call is not a message, but it authenticates with the same key.
| Channel | What it sends | Availability |
|---|---|---|
| WhatsApp Business messaging | Template and session messages delivered over WhatsApp Business. | Unified endpoint |
| SMS | Transactional and promotional SMS with sender ID and route control. | Unified endpoint |
| RCS | Rich, branded messaging for RCS-capable Android devices. | Unified endpoint |
| Templated transactional email with full merge-variable support. | Unified endpoint | |
| Instagram messaging | Direct messages to customers who first reached out on Instagram. | Unified endpoint |
| Voice | Outbound and inbound calling, transcription, and recording. | Dedicated endpoint ยท same key |
Sending a message
POST /messages/send
Send a message over a single channel. The example below delivers a WhatsApp template with two positional variables.
curl -X POST https://slide.synquic.com/api/v1/messages/send \ -H "Authorization: Bearer sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": "+919876543210", "channel": "whatsapp", "template": { "name": "order_confirmation", "languageCode": "en", "variables": { "1": "John", "2": "ORD-12345" } } }'
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Recipient. A phone number in E.164 for WhatsApp, SMS and RCS. An email address for email. An Instagram-scoped user id for Instagram. |
channel | string | Yes | Primary channel to attempt first: whatsapp, sms, rcs, email or instagram. |
fallbackChannels | string[] | No | Ordered channels to try if the primary fails. Phone-based channels only, and every channel named here needs its own send scope on the key. |
message | string | Conditional | Plain text body. Required for any channel that is not sending a template. |
template | object | Conditional | Template name, languageCode and positional variables. Required for a WhatsApp template send. |
sms | object | No | Per-channel overrides applied only when the SMS channel is used, such as senderId. |
Response
Every response carries the channel that actually delivered the message and the full ordered list of attempts, whether or not a fallback was needed.
| Field | Type | Description |
|---|---|---|
status | string | sent when a channel delivered, failed when every channel in the chain failed. |
channelUsed | string | The channel that actually delivered the message. Not necessarily the one requested. |
messageId | string | Provider message id for the delivering channel, for reconciling against logs and webhooks. |
attempts | object[] | Every attempt in order, each with its channel, status and an error string when it failed. Present even on a first-try success. |
Fallback routing
How the chain resolves
Set fallbackChannels and One API walks the chain in order: it attempts the primary channel, and on failure moves to the next entry until one delivers or the list is exhausted. There is no orchestration code on your side and no polling. If every channel fails you get a single 502 carrying the complete attempt log.
channel: "whatsapp" and fallbackChannels: ["sms"] needs both whatsapp:send and sms:send on the key, or it returns 403 naming the missing scope.Worked example
WhatsApp first, SMS if it does not land. The sms object holds overrides that apply only if the SMS leg is actually used.
curl -X POST https://slide.synquic.com/api/v1/messages/send \ -H "Authorization: Bearer sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": "+919876543210", "channel": "whatsapp", "fallbackChannels": ["sms"], "message": "Your order ORD-12345 has shipped!", "template": { "name": "order_shipped", "languageCode": "en" }, "sms": { "senderId": "ACMEIN" } }'
Reading the attempts array
Here WhatsApp failed and SMS delivered. channelUsed is the one that worked, and every attempt is listed in the order it was made, so you can log exactly what happened without a second API call.
{
"status": "sent",
"channelUsed": "sms",
"messageId": "sms_9f2a1c8e7b3d",
"attempts": [
{
"channel": "whatsapp",
"status": "failed",
"error": "Template not delivered to this recipient"
},
{
"channel": "sms",
"status": "sent",
"messageId": "sms_9f2a1c8e7b3d"
}
]
}Authentication and scopes
One API uses the same bearer token as every other endpoint. There is nothing new to provision: a key that already carries the per-channel send scopes works immediately.
curl -X POST https://slide.synquic.com/api/v1/messages/send \ -H "Authorization: Bearer sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": "+919876543210", "channel": "sms", "message": "Hello" }'
| Channel in the request | Scope the key must carry |
|---|---|
whatsapp:send | |
| sms | sms:send |
| rcs | rcs:send |
email:send | |
instagram:send |
Create and scope keys from API keys in the dashboard. Rate limits are shared with the dedicated endpoints rather than additional to them, see rate limits.
Errors
Standard HTTP semantics, plus one outcome specific to this endpoint: a 502 means the chain was walked in full and nothing delivered.
| Status | Meaning | When it happens |
|---|---|---|
400 | Bad request | A required field is missing, or a fallback chain mixes phone-based channels with email or Instagram. |
403 | Missing scope | The key lacks a send scope for a channel named in the request. The body names the exact scope. Every channel in the chain is checked, not just the primary. |
429 | Rate limited | The per-key limit was exceeded. Shared with the dedicated endpoints, not additional to them. |
502 | All channels failed | Every channel in the chain was attempted and none delivered. The full attempts array comes back so you know what to retry. |
FAQ
| Question | Answer |
|---|---|
| Do I need a new API key? | No. The same key and the same channel scopes you already have work with One API today. |
| Can I still use the per-channel APIs? | Yes. One API is additive. Campaigns, templates and per-channel analytics continue to live in the dedicated endpoints. |
| Does a fallback cost more? | You are billed per delivered message at the normal rate for whichever channel delivered it. A failed attempt is not billed. |
| Can email and Instagram be in a chain? | No. They address a different recipient identifier from the phone-based channels and are always sent alone. |
Ready to send?
Create a key, then send your first unified message in a single request.