Kalert Messaging API
The Kalert Messaging API lets you send messages across multiple channels — SMS, Email, and WhatsApp — through a single unified endpoint. Messages are dispatched asynchronously via Kafka, giving you fast acknowledgement and reliable delivery. All sent messages are logged to MongoDB for audit and reporting.
X-API-Key header. The API key determines the authenticated user, controls rate limiting, and is used to validate sender permissions.How it works
When you call POST /api/schedule/send, the API validates your key, deducts credits, queues the message in Kafka, and returns a 202 QUEUED response immediately. The Kafka consumer then picks up the message, dispatches it through the appropriate channel, and logs the outcome (SENT or FAILED) to MongoDB. If dispatch fails, credits are automatically refunded.
Authentication
Every request must include your API key as a request header. Keys are managed in the apikeys table and must have a status of active or enabled to be accepted.
Header
X-API-Key: your-api-key-hereKey statuses
| Status | Behaviour |
|---|---|
| active / enabled | Accepted — requests proceed normally |
| disabled | Rejected with 401 |
| Missing key | Rejected with 401 Invalid API key |
Sender ID Endpoints
Use these endpoints to request sender names, check approval status, and list sender records tied to your API key. Every sender request is created with a pending status and must be approved before it can be used for SMS delivery.
Request a Sender ID
/api/senders/requestCreates a new sender request for the authenticated user. The API saves the sender with pending status immediately.
Required header
X-API-Key: your-api-keyRequest body
{
"senderName": "MySender",
"purpose": "For transaction alerts"
}pending on creation.Check Sender Status
/api/senders/{senderName}/statusReturns the latest approval status for a specific sender name owned by the API key's user. This endpoint refreshes and returns the current senders.status value.
Required header
X-API-Key: your-api-keysenders.status immediately and returns the updated status.List Sender Records
/api/sendersLists all sender records that belong to the authenticated API key user, including their current status and metadata.
Required header
X-API-Key: your-api-keyErrors & Response Codes
All error responses follow a consistent JSON structure:
Error response body
{
"status": 401,
"message": "Invalid API key",
"timestamp": "2026-03-27T10:00:00Z"
}| Code | Meaning | Common cause |
|---|---|---|
| 202 | Accepted | Message queued successfully |
| 200 | OK | Log query returned successfully |
| 400 | Bad request | Missing or invalid request body fields |
| 401 | Unauthorized | Invalid, missing, or disabled API key; unapproved sender |
| 402 | Payment required | Insufficient credits to dispatch the message |
| 429 | Too many requests | Rate limit of 100 req/min exceeded |
| 500 | Server error | Unexpected internal error |
Rate Limiting
Each API key is limited to 100 requests per minute using a token bucket algorithm. The bucket refills fully every 60 seconds.
429 Response example
{
"status": 429,
"message": "Rate limit exceeded. Retry after 43s",
"timestamp": "2026-03-27T10:00:00Z"
}Credits & Billing
Credits are deducted immediately when the message is queued. If delivery fails, the deducted credits are automatically refunded. The amount depends on channel and message size.
Credit rates by channel
| Channel | Rate | Notes |
|---|---|---|
| SMS | 1 credit per 160-char segment | Long messages are split into segments and billed per segment |
| 2 credits per message | Each send counts as 2 credits regardless of length | |
| 1 credit per email | Single email send is always 1 credit |
Credit types (deduction order)
The system deducts from credit pools in the order configured in the creditusage table — typically: expiry balance first, then bonus, then non-expiry.
| Pool | Field | Notes |
|---|---|---|
| Expiry balance | expirybalance | Time-limited credits, used first |
| Bonus balance | bonusbalance | Promotional credits, used second |
| Non-expiry balance | nonexpirybalance | Permanent credits, used last |
402 Payment Required and no message is queued.Send a Message
Validates the API key, deducts credits, and publishes the message to Kafka. Returns 202 as soon as the message is queued. Actual delivery happens asynchronously.
/api/messages/sendHeaders
| Header | Required | Description |
|---|---|---|
| X-API-Key | required | Your API key |
| Content-Type | required | application/json |
Request body
| Field | Type | Description |
|---|---|---|
| channel | string | SMS, EMAIL, or WHATSAPP |
| recipient | string | Phone number (SMS/WhatsApp) or email; use recipients for bulk |
| recipients | array | List of recipients for bulk delivery |
| body | string | Message content |
| senderName | string | Required for SMS — Sender ID, max 11 chars, registered in senders table |
| subject | string | Email subject — only when channel is EMAIL |
| isSchedule | boolean | For /api/schedule/send — enable future delivery |
| scheduleDate | string | When isSchedule is true — YYYY-MM-DD HH:MM:SS |
| recursion | string | daily, weekly, or monthly for recurring schedules |
SMS example
senderName must match an entry in the senders table that belongs to your account and has status approved. Max 11 characters.Request
POST https://springback.kalert.net/api/messages/send
X-API-Key: your-api-key
Content-Type: application/json
{
"channel": "SMS",
"recipient": "233592548849",
"senderName": "Sender ID",
"body": "Hello from Kalert API!",
"isSchedule": false,
"scheduleDate": ""
}Response — 202 Accepted
{
"status": "QUEUED",
"message": "Your message has been queued for delivery",
"channel": "SMS",
"recipient": "233592548849"
}Email example
Request
POST https://springback.kalert.net/api/messages/send
X-API-Key: your-api-key
Content-Type: application/json
{
"channel": "EMAIL",
"recipient": "user@example.com",
"subject": "Your Kalert notification",
"body": "This is a test email from the Kalert API.",
"isSchedule": false,
"scheduleDate": ""
}Response — 202 Accepted
{
"status": "QUEUED",
"message": "Your message has been queued for delivery",
"channel": "EMAIL",
"recipient": "user@example.com"
}WhatsApp example
Request
POST https://springback.kalert.net/api/messages/send
X-API-Key: your-api-key
Content-Type: application/json
{
"channel": "WHATSAPP",
"recipient": "+233592548849",
"body": "Hello via WhatsApp from Kalert!",
"isSchedule": false,
"scheduleDate": ""
}Response — 202 Accepted
{
"status": "QUEUED",
"message": "Your message has been queued for delivery",
"channel": "WHATSAPP",
"recipient": "+233592548849"
}Error scenarios
401 — Invalid / missing API key
{ "status": 401, "message": "Invalid API key", "timestamp": "..." }401 — API key disabled
{ "status": 401, "message": "API key is disabled", "timestamp": "..." }401 — Unapproved or unknown sender (SMS)
{ "status": 401, "message": "Sender 'MySender' not found, not owned by this user, or not approved", "timestamp": "..." }402 — Insufficient credits
{ "status": 402, "message": "Insufficient credits. Need 1 units but balance is insufficient.", "timestamp": "..." }400 — Validation error
{
"status": 400,
"message": "Validation failed",
"errors": {
"body": "Message body is required",
"channel": "Channel is required"
}
}429 — Rate limit exceeded
{ "status": 429, "message": "Rate limit exceeded. Retry after 43s", "timestamp": "..." }Bulk sending
Direct bulk sending uses /api/messages/send. For scheduled bulk delivery, use /api/schedule/send instead. Provide a top-level channel and a recipients array for bulk delivery.
Request body (example)
Request
POST https://springback.kalert.net/api/messages/send
X-API-Key: your-api-key
Content-Type: application/json
{
"channel": "SMS",
"recipients": ["0592548849", "0208894620"],
"senderName": "Sender ID",
"body": "From Kalert API!",
"isSchedule": false,
"scheduleDate": ""
}Notes
- For a single SMS you may use
recipientas a string. For bulk SMS provide a top-levelrecipientsarray. The server detects which one is present and dispatches accordingly. senderNameis validated against your account (must be an approved sender name) and is used as the SMS sender ID (max 11 chars).- Credits are deducted per recipient at the time of queuing (1 unit per recipient). If dispatch fails the credits are refunded.
- This endpoint is for immediate send-only requests. To schedule a message for future delivery, use
/api/schedule/sendand setisScheduleto true with a validscheduleDate(in YYYY-MM-DD HH:MM:SS or ISO-8601 format).
Response — 202 Accepted
{
"accepted": 2,
"rejected": 0,
"details": [
{ "recipient": "0241234567", "status": "QUEUED" },
{ "recipient": "0201234567", "status": "QUEUED" }
]
}Scheduled sending (via /api/schedule/send)
Scheduling uses the /api/schedule/send endpoint. Set isSchedule to true and provide an explicit scheduleDate. The request will be accepted and the message stored for delivery at the requested time.
Request (example)
POST https://springback.kalert.net/api/schedule/send
X-API-Key: your-api-key
Content-Type: application/json
{
"channel": "SMS",
"recipients": ["0592548849"],
"senderName": "Sender ID",
"body": "Scheduled reminder",
"isSchedule": true,
"scheduleDate": "2026-04-01 13:30:00",
"recursion": "daily"
}Notes
- Provide
scheduleDatein YYYY-MM-DD HH:MM:SS (server-local or UTC depending on server config); ISO-8601 is also accepted if supported. - When scheduled, the API returns a scheduling confirmation and a
scheduleIdyou can use to reference or cancel the scheduled item.
Response — 202 Accepted
{
"scheduleId": "abc123",
"status": "SCHEDULED",
"sendAt": "2023-10-30 17:56:00"
}Update before due
/api/schedule/5PUT https://springback.kalert.net/api/schedule/5
X-API-Key: your-api-key
Content-Type: application/json
{
"channel": "SMS",
"recipients": ["0531907426"],
"senderName": "Sender ID",
"body": "Updated message content",
"isSchedule": true,
"scheduleDate": "2026-04-02 09:00:00"
}Cancel
/api/schedule/5DELETE https://springback.kalert.net/api/schedule/5
X-API-Key: your-api-keyGet All Message Logs
Request
GET https://springback.kalert.net/api/messages/logs
X-API-Key: your-api-keyResponse — 200 OK
[
{
"id": "65f1a2b3c4d5e6f7a8b9c0d1",
"userId": 3,
"channel": "SMS",
"recipient": "233592548849",
"subject": null,
"body": "Hello from Kalert API!",
"status": "SENT",
"sentAt": "2026-03-27T11:25:38Z",
"errorMessage": null
}
]Get Logs by Channel
Path parameter
| Parameter | Values |
|---|---|
| channel | SMS EMAIL WHATSAPP (case-insensitive) |
Request
GET https://springback.kalert.net/api/messages/logs/channel/SMS
X-API-Key: your-api-keyResponse — 200 OK
[
{
"id": "65f1a2b3c4d5e6f7a8b9c0d1",
"userId": 3,
"channel": "SMS",
"recipient": "233592548849",
"body": "Hello from Kalert API!",
"status": "SENT",
"sentAt": "2026-03-27T11:25:38Z"
}
]Get Logs by Status
Path parameter
| Parameter | Values |
|---|---|
| status | SENT or FAILED (case-insensitive) |
Request — fetch failed messages
GET https://springback.kalert.net/api/messages/logs/status/FAILED
X-API-Key: your-api-keyResponse — 200 OK
[
{
"id": "65f1a2b3c4d5e6f7a8b9c0d2",
"userId": 3,
"channel": "SMS",
"recipient": "233592548849",
"body": "Hello!",
"status": "FAILED",
"sentAt": "2026-03-27T11:00:00Z",
"errorMessage": "mNotify SMS error: Insufficient balance"
}
]Get Logs by User
userId in the path does not match the user associated with the provided API key, the request returns 403 Forbidden.Request
GET https://springback.kalert.net/api/messages/logs/user/3
X-API-Key: your-api-keyResponse — 200 OK
[
{
"id": "65f1a2b3c4d5e6f7a8b9c0d1",
"userId": 3,
"channel": "EMAIL",
"recipient": "user@example.com",
"subject": "Test",
"status": "SENT",
"sentAt": "2026-03-27T10:52:46Z"
}
]Response — 403 Forbidden (wrong user)
{ "status": 403, "message": "Forbidden" }Get Logs by Recipient
Request
GET https://springback.kalert.net/api/messages/logs/recipient/233592548849
X-API-Key: your-api-keyResponse — 200 OK
[
{
"id": "65f1a2b3c4d5e6f7a8b9c0d3",
"userId": 3,
"channel": "SMS",
"recipient": "233592548849",
"body": "Hello from Kalert API!",
"status": "SENT",
"sentAt": "2026-03-27T11:25:38Z"
}
]