Developers

ZafeConnect API documentation

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.

REST · JSON · api.zafeconnect.com

Getting started

Authenticate and send

Base URL

https://api.zafeconnect.com. Every endpoint is prefixed with /v1.

Authentication

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.

Format

All requests and responses are JSON, UTF-8 encoded. Send Content-Type: application/json on requests with a body.

Charges

Each message is charged in your account currency from your prepaid balance, or invoiced monthly for enterprise customers.

Limits

LimitValue
Send rate1,000 requests per minute per key
BulkUp to 10,000 messages per request, 100 requests per minute
FormatJSON, UTF-8
AuthenticationX-API-Key header

Reference

Endpoints

POST/v1/messages/send

Send a single message via SMS, RCS, WhatsApp or email.

FieldTypeRequiredDescription
tostringyesRecipient phone number (E.164) or email address
channelstringyesPrimary channel: sms, rcs, whatsapp or email (or use channels[])
channelsarray—Channels in priority order; the first is primary, the rest are fallbacks
messagestringyesPlain-text body. Used as-is on SMS; text fallback on HTML channels
htmlmessagestring—HTML body (email only); ignored on SMS and WhatsApp
senderstring—Sender ID. Must be an approved sender. The 11-character alphanumeric limit applies to SMS only
subjectstring—Email subject (email channel only)
fallbackarray—Backup channels tried if the primary fails, for example ["sms","email"]
scheduled_atISO 8601—Send time. No offset means UTC. A trailing Z or an explicit offset both work
Request
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"
  }'
Response
{
  "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.

POST/v1/messages/bulk

Send up to 10,000 messages in one request. Each is queued individually. Limited to 100 requests per minute.

FieldTypeRequiredDescription
messagesarrayyesArray of message objects with the same fields as /send (max 10,000)
Request
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!"}
    ]
  }'
Response
{
  "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

GET/v1/messages/:id

Get the current delivery status of a message by its ID.

FieldTypeRequiredDescription
idstringyesMessage ID returned by /send or /bulk
Request
curl -X GET https://api.zafeconnect.com/v1/messages/msg_01j2abc \
  -H "X-API-Key: YOUR_API_KEY"
Response
{
  "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

DELETE/v1/messages/:id

Cancel a scheduled message. Only works while the status is scheduled.

FieldTypeRequiredDescription
idstringyesMessage ID of a scheduled message
Request
curl -X DELETE https://api.zafeconnect.com/v1/messages/msg_01j2abc \
  -H "X-API-Key: YOUR_API_KEY"
Response
{ "message": "Message cancelled", "message_id": "msg_01j2abc..." }

200 Cancelled404 Not found409 Already sent

GET/v1/account/balance

Get your current account balance, currency and plan.

Request
curl -X GET https://api.zafeconnect.com/v1/account/balance \
  -H "X-API-Key: YOUR_API_KEY"
Response
{ "balance": 1240.50, "currency": "EUR", "plan": "business", "updated_at": "2026-05-27T12:00:00Z" }

200 OK401 Unauthorized

GET/v1/account/pricing

Get your current prices per channel and country.

FieldTypeRequiredDescription
channelstring—Filter: sms, rcs, whatsapp or email
Request
curl -X GET https://api.zafeconnect.com/v1/account/pricing \
  -H "X-API-Key: YOUR_API_KEY"
Response
{ "pricing": [
  { "channel": "sms", "country": "ALL", "price": 0.00, "currency": "EUR" }
] }

200 OK401 Unauthorized

GET/v1/account/usage

Message volume and spend for a date range, broken down by channel.

FieldTypeRequiredDescription
fromdate—Start date, YYYY-MM-DD (default: 30 days ago)
todate—End date, YYYY-MM-DD (default: today)
Request
curl -X GET https://api.zafeconnect.com/v1/account/usage?from=2026-05-01 \
  -H "X-API-Key: YOUR_API_KEY"
Response
{ "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

Send a message in your language

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
curl
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"
  }'
JavaScript
JavaScript
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);
Python
Python
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
PHP
<?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'];
C#
C#
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

Delivery events

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.

FieldTypeRequiredDescription
eventstringyesmessage.delivered, message.failed, message.opened or message.clicked
message_idstringyesThe message ID
statusstringyesdelivered, failed, opened or clicked
delivered_atISO 8601—Delivery timestamp
error_codestring—Error code, when the message failed
Request
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.

Verifying the signature

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.

Node.js
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).

Need help integrating?

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.