Start free
NewJourneys through the APIExplore

One key, one webhook and every channel.

WhatsApp, SMS, RCS, email and push through the same REST API: the same authentication, the same error format and the same signed webhooks for statuses and replies. Test with a sandbox key before you send for real.

US$5 test credit when you sign up. Test keys never spend it.

Public OpenAPI documentTypeScript SDK and CLISigned webhooks
Send a WhatsApp templatePOST /messages
curl -X POST https://api.messages.ccxcompany.co/messages \  -H "Authorization: Bearer $CCX_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: order-4821" \  -d '{    "numberId": "num_…",    "to": "+14155550123",    "type": "template",    "templateId": "tpl_…",    "templateVars": ["Marina", "4821"],    "metadata": { "order": "4821" }  }'
201{ "id": "msg_…", "status": "queued" }
your-system.com/webhooks/ccx
  • message.sentSigned · 200Sent to +1 415 555 0123
  • message.deliveredSigned · 200Delivered · metadata.order 4821
  • message.readSigned · 200Read by the customer
  • message.receivedSigned · 200Customer replied: “Thanks, see you today!”
Sample request and webhook events. Values with … are placeholders.

One send. One response.

The WhatsApp route takes the sending number, the recipient, the type and the content. The message is queued right away, and its status reaches your webhook later. Pick a field to see it in the call.

POST /messages
curl -X POST https://api.messages.ccxcompany.co/messages \  -H "Authorization: Bearer $CCX_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: order-4821" \  -d '{    "numberId": "num_…",    "to": "+14155550123",    "type": "template",    "templateId": "tpl_…",    "templateVars": ["Marina", "4821"],    "metadata": { "order": "4821" }  }'
201{ "id": "msg_…", "status": "queued", … }

Outside the 24-hour window, WhatsApp only accepts approved templates. The full message object is in openapi.json.

Each channel has its own route. Everything else is the same.

The body changes by channel. The key, the error format and the webhooks work the same way on all of them.

POST/messagesSend a text, template, media or interactive message.
GET/messagesList the messages you sent and received.
GET/messages/{id}Read one message and its status, if you'd rather poll.
WhatsAppThe official WhatsApp Business Platform, through Meta.Outside the 24-hour window, approved templates only.message.sent, delivered, read, failed and received reach your webhook.
One key per organization, with scopesEach key can only do what its scopes allow, and goes in the Authorization: Bearer header.
Idempotency-Key on sendsOn WhatsApp, SMS and push, repeating a send with the same key returns the original message instead of creating another.
Errors as Problem Detailsapplication/problem+json, with a stable code, the status and a traceId so you can find the call.
429 with Retry-AfterPast the request limit, the response says how long to wait before trying again.

Statuses and replies reach you, signed.

Subscribe a URL and pick the events. Before it goes live, CCX checks the address and sends a signed ping.

X-CCX-SignatureHMAC-SHA256 of the timestamp and the body, with your secret, which you can rotate any time.
X-CCX-Idempotency-KeySo your system can drop repeated deliveries.
32 events to subscribe toDelivery, read, failure and reply events for WhatsApp, SMS, RCS, email and Instagram, plus balance alerts.
Answer within 10 sQueue the work and return 2xx right away. Anything slower counts as a failure.
What happens when your endpoint answers
Retried five more timesA 5xx means try again: after 5 s, 30 s, 2 min, 10 min and 1 h.
  1. 5 s
  2. 30 s
  3. 2 min
  4. 10 min
  5. 1 h
POST https://your-system.com/webhooks/ccxX-CCX-Signature: t=1791300000,v1=5f2c…X-CCX-Event: message.deliveredX-CCX-Delivery-Id: …X-CCX-Idempotency-Key: … {  "event": "message.delivered",  "messageId": "msg_…",  "channel": "whatsapp",  "metadata": { "order": "4821" },  "occurredAt": "2026-10-06T14:02:11Z"}

Sample webhook delivery, trimmed.

Test all you want. Send for real when you're ready.

A test key behaves like a production key, but sends go through a simulated transport and draw from a test wallet you top up for free in the dashboard. In the playground, every public route becomes a form you run from the browser.

A test key never touches Meta or your balance.
In the playground, connecting a production key asks you to confirm that sends will be real.
The OpenAPI document is public at ccxmessages.com/openapi.json.
TypeScript SDK and CLI on npm: @ccx-messages/sdk and @ccx-messages/cli.
Where the call goes
Your callSame routes, headers and errors
TransportSimulated
WalletTest wallet, topped up for free
ReachesNobody
Nothing reaches a customer and nothing is really charged.

Contacts, lists, journeys and pipeline, with the same key.

The public contract has 125 operations, all in the OpenAPI document.

Contacts and opt-out

Create, update, bulk import and record who asked not to be contacted.

/contacts/contacts/import

Lists and segments

Static lists and saved segments, for campaigns and journeys.

/v2/lists/v2/segments

Journeys and events

Create journeys, add contacts to them and record purchases for attribution.

/v2/journeys/v2/events

Sales pipeline

Pipelines, stages and cards, with fields, activities and files.

/pipelines

Templates and flows

Create draft templates and read WhatsApp templates and flows.

/v2/templates/v2/flows

Exports, balance and numbers

Export each channel's messages by period, and check your balance and each number's health.

/v2/exports/{channel}/wallet
See the reference for every route

API calls are free. You pay for messages.

Each message goes out at its channel's published price. The monthly plan covers your whole team, with no per-seat fees.

See full pricing
What you usePrice1,000 messages
API callsEvery route, every keyNo chargeNo charge
WhatsApp utilityTo the United StatesUS$0.0084US$8.40
EmailAny destinationUS$0.0004US$0.40
Push notificationAPNs, FCM or Web PushUS$0.0002US$0.20
Monthly planYour whole team, no seat feesfrom US$39/moFlat, per month

International price list in US dollars, euros for eurozone destinations. WhatsApp is priced by destination country and message category. SMS is priced for Brazilian numbers; international routes are on request.

What engineering teams ask

Stuck on a route? Talk to the people who run the API.

Is the API versioned in the path?

There's no single prefix: WhatsApp sending is POST /messages and newer channels live under /v2, like /v2/sms/messages. The public OpenAPI document is the reference for every route.

Do I need the SDK?

No. It's REST with JSON, and the cURL above is enough. The TypeScript SDK generates the Idempotency-Key for you and returns typed errors.

How does idempotency work?

Send the Idempotency-Key header with a value of your own, like the order number. On WhatsApp, the same key within 24 hours returns the original message; the same key with a different body returns 409.

What's the rate limit?

Past the limit, the API answers 429 RATE_LIMITED with a Retry-After header telling you when to try again. If your volume needs more, talk to us.

How do I get delivery status?

Through signed webhooks, with the events you pick. If you'd rather poll, GET /messages/{id} returns the message's status.

Do you support SMPP for SMS?

Yes, over a TLS-encrypted connection, for teams that already have an SMPP client and don't want to rewrite it. CCX's SMS goes to Brazilian numbers.

Where is the full reference?

In the public OpenAPI document at ccxmessages.com/openapi.json. Anything not in it isn't contract and may change.

Your first call starts with a test key.

Sign up through the site and get a US$5 test credit.