Base URL
https://your-domain.com/api/v1
All endpoints are prefixed with /api/v1. HTTPS only. All request and response bodies are JSON.
Authentication
Two auth methods are supported — use either in the Authorization header:
Authorization: Bearer <accessToken>From POST /auth/login. Expires 15 min. Rotate via POST /auth/refresh.
Authorization: ApiKey <key>Generated in the dashboard. Shown only once on creation. Never expires unless revoked.
Quick start
Register → get an API key → send your first SMS in under 5 minutes:
# 1. Get an access token
curl -X POST https://your-domain.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@org.com","password":"yourpassword"}'
# Response:
# { "accessToken": "eyJ...", "refreshToken": "..." }
# 2. Send an SMS
curl -X POST https://your-domain.com/api/v1/sms/send \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{"to":"+256700000000","message":"Hello from MARIPA E-SYNC!"}'Send SMS
/sms/sendSend a single SMS. Cost is deducted from your wallet on dispatch.
tostringrequiredRecipient phone in E.164 format, e.g. +256700000000messagestringrequiredSMS body. Max 1600 chars (10 segments).senderIdstringoptionalApproved sender ID. Defaults to your tenant default.{
"status": "SENT",
"messageId": "msg_01j9k3xhf7abc",
"to": "+256700000000",
"cost": "$0.035",
"segments": 1,
"sentAt": "2026-06-07T08:12:03Z"
}Bulk send
/sms/bulkSend the same message to multiple recipients. Each number gets an individual delivery record.
# Request
{
"to": ["+256700000001", "+256700000002", "+254712345678"],
"message": "Your promo code is JUNE20. Valid today only.",
"senderId": "MARIPA"
}
# Response
{
"queued": 3,
"totalCost": "$0.105",
"batchId": "batch_abc123",
"results": [
{ "to": "+256700000001", "status": "SENT", "messageId": "msg_01..." },
{ "to": "+256700000002", "status": "SENT", "messageId": "msg_02..." },
{ "to": "+254712345678", "status": "SENT", "messageId": "msg_03..." }
]
}OTP
/sms/otpSend a one-time password. The platform generates a 6-digit code and sends it. You verify the code via /sms/otp/verify.
# Send OTP
POST /sms/otp
{ "to": "+256700000000", "purpose": "login" }
# Response
{ "otpId": "otp_xyz789", "expiresIn": 300 }
# Verify OTP
POST /sms/otp/verify
{ "otpId": "otp_xyz789", "code": "847291" }
# Response
{ "valid": true }Campaigns
# Create a campaign
POST /campaigns
{
"name": "June Promotion",
"message": "Get 20% off today! Use JUNE20",
"groupIds": ["grp_abc123"]
}
# → { "id": "cmp_...", "status": "DRAFT" }
# Send it
POST /campaigns/{id}/send
# Track delivery
GET /campaigns/{id}
# → { "delivered": 4821, "failed": 12, "pending": 0 }Errors
All errors return { "error": "..." } with a standard HTTP status code.
400Bad RequestMissing or invalid parameters.401UnauthorizedMissing or invalid Authorization header.402Payment RequiredInsufficient wallet balance. Top up and retry.403ForbiddenYour account lacks permission for this action.422Unprocessable EntityValid JSON but failed business rules (e.g. invalid phone, blocked sender ID).429Too Many RequestsRate limit exceeded. Check the Retry-After header.500Server ErrorOur fault. Contact support with your request-id.Rate limits
Applied per tenant. Limits reset every 60 seconds. Exceeded requests return 429.
100 req / min1,000 SMS / day500 req / min10,000 SMS / dayCustomUnlimitedWebhooks — Delivery report
Configure a delivery report URL in your tenant settings. We POST a JSON payload when a message status changes.
# Payload sent to your webhook URL
{
"event": "delivery.report",
"messageId": "msg_01j9k3xhf7abc",
"to": "+256700000000",
"status": "DELIVERED",
"timestamp": "2026-06-07T08:12:04Z",
"latencyMs": 487
}
# Possible statuses: SENT DELIVERED FAILED EXPIREDWebhook signature
Every webhook request includes X-E-Sync-Signature — an HMAC-SHA256 of the raw body, signed with your webhook secret.
// Node.js verification
const crypto = require('crypto');
function isValidSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Acceptable use policy
Data retention
90 daysContent, recipient, status, cost.90 daysPer-message timestamps and status changes.30 daysPath, status code, latency — no body logging.1 yearAuth events, settings changes, wallet transactions.Until deletedRetained until you delete them.