Base URL
https://api.zafeconnect.com. Every endpoint is prefixed with /v1.
Developers
Developer documentation for the multi-channel messaging API: SMS, RCS, WhatsApp and email. Get an API key in your ZafeConnect account, send your first message in minutes, and follow delivery with webhooks.
Getting started
https://api.zafeconnect.com. Every endpoint is prefixed with /v1.
Send your API key in the X-API-Key header on every request. Create and manage keys in your ZafeConnect account under API Keys. Keys come in live and test environments.
All requests and responses are JSON, UTF-8 encoded. Send Content-Type: application/json on requests with a body.
Each message is charged in your account currency from your prepaid balance, or invoiced monthly for enterprise customers.
| Limit | Value |
|---|---|
Send rate | 1,000 requests per minute per key |
Bulk | Up to 10,000 messages per request, 100 requests per minute |
Format | JSON, UTF-8 |
Authentication | X-API-Key header |
Reference
/v1/messages/sendSend a single message via SMS, RCS, WhatsApp or email.
| Field | Type | Required | Description |
|---|---|---|---|
to | string | yes | Recipient phone number (E.164) or email address |
channel | string | yes | Primary channel: sms, rcs, whatsapp or email (or use channels[]) |
channels | array | — | Channels in priority order; the first is primary, the rest are fallbacks |
message | string | yes | Plain-text body. Used as-is on SMS; text fallback on HTML channels |
htmlmessage | string | — | HTML body (email only); ignored on SMS and WhatsApp |
sender | string | — | Sender ID. Must be an approved sender. The 11-character alphanumeric limit applies to SMS only |
subject | string | — | Email subject (email channel only) |
fallback | array | — | Backup channels tried if the primary fails, for example ["sms","email"] |
scheduled_at | ISO 8601 | — | Send time. No offset means UTC. A trailing Z or an explicit offset both work |
curl -X POST https://api.zafeconnect.com/v1/messages/send \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+4520123456",
"channel": "sms",
"message": "Hello from ZafeConnect!",
"sender": "MyBrand"
}'{
"message_id": "msg_01j2abc...",
"status": "queued",
"channel": "sms",
"to": "+4520123456",
"cost": 0.00,
"currency": "EUR",
"created_at": "2026-05-27T12:00:00Z"
}201 Queued400 Invalid params401 Invalid API key402 Insufficient balance
The sender must be an approved sender. See how SenderID registration works. The cost and currency fields reflect your account pricing; example values are illustrative.
/v1/messages/bulkSend up to 10,000 messages in one request. Each is queued individually. Limited to 100 requests per minute.
| Field | Type | Required | Description |
|---|---|---|---|
messages | array | yes | Array of message objects with the same fields as /send (max 10,000) |
curl -X POST https://api.zafeconnect.com/v1/messages/bulk \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"to":"+4520123456","channel":"sms","message":"Hi Anders!"},
{"to":"+4531234567","channel":"sms","message":"Hi Sara!"}
]
}'{
"batch_id": "b_01j2...",
"queued": 2,
"failed": 0,
"estimated_cost": 0.00,
"currency": "EUR",
"created_at": "2026-05-27T12:00:00Z"
}201 Accepted400 Invalid params413 Over 10,000 messages
/v1/messages/:idGet the current delivery status of a message by its ID.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Message ID returned by /send or /bulk |
curl -X GET https://api.zafeconnect.com/v1/messages/msg_01j2abc \
-H "X-API-Key: YOUR_API_KEY"{
"message_id": "msg_01j2abc...",
"status": "delivered",
"channel": "sms",
"to": "+4520123456",
"delivered_at": "2026-05-27T12:00:03Z",
"cost": 0.00,
"currency": "EUR"
}200 OK404 Not found
/v1/messages/:idCancel a scheduled message. Only works while the status is scheduled.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Message ID of a scheduled message |
curl -X DELETE https://api.zafeconnect.com/v1/messages/msg_01j2abc \
-H "X-API-Key: YOUR_API_KEY"{ "message": "Message cancelled", "message_id": "msg_01j2abc..." }200 Cancelled404 Not found409 Already sent
/v1/account/balanceGet your current account balance, currency and plan.
curl -X GET https://api.zafeconnect.com/v1/account/balance \
-H "X-API-Key: YOUR_API_KEY"{ "balance": 1240.50, "currency": "EUR", "plan": "business", "updated_at": "2026-05-27T12:00:00Z" }200 OK401 Unauthorized
/v1/account/pricingGet your current prices per channel and country.
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | — | Filter: sms, rcs, whatsapp or email |
curl -X GET https://api.zafeconnect.com/v1/account/pricing \
-H "X-API-Key: YOUR_API_KEY"{ "pricing": [
{ "channel": "sms", "country": "ALL", "price": 0.00, "currency": "EUR" }
] }200 OK401 Unauthorized
/v1/account/usageMessage volume and spend for a date range, broken down by channel.
| Field | Type | Required | Description |
|---|---|---|---|
from | date | — | Start date, YYYY-MM-DD (default: 30 days ago) |
to | date | — | End date, YYYY-MM-DD (default: today) |
curl -X GET https://api.zafeconnect.com/v1/account/usage?from=2026-05-01 \
-H "X-API-Key: YOUR_API_KEY"{ "summary": { "total_messages": 4820, "total_cost": 0.00, "currency": "EUR" },
"breakdown": { "sms": { "messages": 4200, "cost": 0.00 }, "email": { "messages": 620, "cost": 0.00 } } }200 OK401 Unauthorized
Code samples
The same documented call as above, written out in five languages. They are plain HTTP requests that work without a client library; the values are examples.
curl -X POST https://api.zafeconnect.com/v1/messages/send \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+4520123456",
"channel": "sms",
"message": "Hello from ZafeConnect!",
"sender": "MyBrand"
}'const res = await fetch('https://api.zafeconnect.com/v1/messages/send', {
method: 'POST',
headers: { 'X-API-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
"to": "+4520123456",
"channel": "sms",
"message": "Hello from ZafeConnect!",
"sender": "MyBrand"
}),
});
const data = await res.json();
console.log(data.message_id, data.status);import requests
res = requests.post(
'https://api.zafeconnect.com/v1/messages/send',
headers={'X-API-Key': 'YOUR_API_KEY'},
json={
"to": "+4520123456",
"channel": "sms",
"message": "Hello from ZafeConnect!",
"sender": "MyBrand"
},
)
data = res.json()
print(data['message_id'], data['status'])<?php
$ch = curl_init('https://api.zafeconnect.com/v1/messages/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['X-API-Key: YOUR_API_KEY', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'to' => '+4520123456',
'channel' => 'sms',
'message' => 'Hello from ZafeConnect!',
'sender' => 'MyBrand',
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
echo $data['message_id'] . ' ' . $data['status'];using System.Net.Http;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "YOUR_API_KEY");
var payload = JsonSerializer.Serialize(new {
to = "+4520123456",
channel = "sms",
message = "Hello from ZafeConnect!",
sender = "MyBrand"
});
var res = await client.PostAsync("https://api.zafeconnect.com/v1/messages/send", new StringContent(payload, Encoding.UTF8, "application/json"));
Console.WriteLine(await res.Content.ReadAsStringAsync());Webhooks
ZafeConnect sends a POST to your webhook URL when a message status changes. Set the URL in your API Keys settings. Each request carries three headers: X-ZafeConnect-Event, X-ZafeConnect-Timestamp and X-ZafeConnect-Signature.
| Field | Type | Required | Description |
|---|---|---|---|
event | string | yes | message.delivered, message.failed, message.opened or message.clicked |
message_id | string | yes | The message ID |
status | string | yes | delivered, failed, opened or clicked |
delivered_at | ISO 8601 | — | Delivery timestamp |
error_code | string | — | Error code, when the message failed |
POST https://your-app.com/webhooks/zafeconnect
X-ZafeConnect-Event: message.delivered
X-ZafeConnect-Timestamp: 1748345600000
X-ZafeConnect-Signature: 9f86d081884c7d659a2feaa0c55ad015...
Content-Type: application/json
{
"event": "message.delivered",
"message_id": "msg_01j2abc...",
"status": "delivered",
"channel": "sms",
"to": "+4520123456",
"delivered_at": "2026-05-27T12:00:03Z"
}Return any 2xx status to acknowledge. ZafeConnect retries up to 5 times if your endpoint does not acknowledge.
The signature is the hex HMAC-SHA256 of the string {timestamp}.{raw_request_body}, using your webhook signing secret. Compute it over the exact bytes received and compare it to the header.
const crypto = require("crypto");
const ts = req.headers["x-zafeconnect-timestamp"];
const body = req.rawBody; // the exact bytes received
const expected = crypto.createHmac("sha256", SIGNING_SECRET)
.update(ts + "." + body)
.digest("hex");
const ok = expected === req.headers["x-zafeconnect-signature"];Further endpoints exist, for example message history and resend, sender rules and validation, email domains and analytics. Contact us for the complete reference.
A machine-readable OpenAPI 3.1 description of the endpoints above is available as openapi.json, and a Postman collection with the same requests can be imported into Postman (set the apiKey variable).
Tell us about your systems. We will help you get your first message out, and you could be live in as little as 2 weeks.