# API documentation Integrate SMS sending, balance checks and delivery tracking with Nisend. Base URL: https://nisend.pakismart.co.tz/v1 Authentication: Authorization: Bearer $NISEND_API_KEY Keep API keys on your server. All examples are illustrative; replace recipients and sender names. ## Quick start Create an [API key](https://messaging.pakismart.co.tz/login), add SMS credits and use an approved [Sender ID](https://messaging.pakismart.co.tz/login). Keep your API key on your server, outside browser code and source control. New keys include sending, message status and workspace read access. Base URL: **https://nisend.pakismart.co.tz/v1** ## Send your first SMS Use the approved sender name in senderId, for example PAX CARGO. Names are matched without case sensitivity and must belong to this workspace. Omit it only if a platform default sender is configured. A 202 response means queued, not delivered. ~~~bash curl -X POST "https://nisend.pakismart.co.tz/v1/api/messages/sms" \ -H "Authorization: Bearer $NISEND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": "255712345678", "text": "Hello from Nisend!", "senderId": "PAX CARGO", "idempotencyKey": "order_123_confirmation" }' ~~~ ~~~javascript const response = await fetch("https://nisend.pakismart.co.tz/v1/api/messages/sms", { method: "POST", headers: { Authorization: "Bearer " + process.env.NISEND_API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ to: "255712345678", text: "Hello from Nisend!", senderId: "PAX CARGO", idempotencyKey: "order_123_confirmation" }) }); const result = await response.json(); if (!response.ok) throw new Error(JSON.stringify(result)); console.log(result.message.id, result.message.status); ~~~ ## Check SMS balance ~~~bash curl "https://nisend.pakismart.co.tz/v1/api/balance" -H "Authorization: Bearer $NISEND_API_KEY" ~~~ Returns workspaceId, balance and unit (SMS segments). Balance is available credits after reservations, not money. Multipart and Unicode messages can use more than one credit per recipient. ## Find approved senders ~~~bash curl "https://nisend.pakismart.co.tz/v1/api/senders" -H "Authorization: Bearer $NISEND_API_KEY" ~~~ Returns senders with senderId (the name) and status, plus defaultSender separately. Pending and suspended senders are excluded. Use the sender name in your send requests. ## Send bulk SMS ~~~bash curl -X POST "https://nisend.pakismart.co.tz/v1/api/messages/sms/bulk" \ -H "Authorization: Bearer $NISEND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": ["255712345678", "255712345679"], "text": "Your shipment is ready.", "senderId": "PAX CARGO", "idempotencyKey": "shipment_batch_001" }' ~~~ Returns 202 with batchId, recipientCount, credits and trackingUrl. Credits are reserved atomically for the entire batch. Invalid recipients or insufficient balance reject the entire request. Combine to (an array of numbers), contactIds and groupIds from this workspace. At least one recipient source is required. Numbers are normalized and duplicates receive one SMS. Maximum 5,000 unique recipients and 200 groups per batch. Retrying the same payload and idempotency key returns the original batch even if group membership has changed. ## Estimate SMS credits ~~~bash curl -X POST "https://nisend.pakismart.co.tz/v1/api/messages/estimate" \ -H "Authorization: Bearer $NISEND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": ["255712345678", "255712345679"], "text": "Your shipment is ready.", "senderId": "PAX CARGO" }' ~~~ Returns recipientCount, segmentsPerMessage, credits, balance, sufficientBalance, sender and sendingAvailable. Sends nothing and reserves no credits. This estimates SMS credits, not a TZS price. Sending checks the recipients and balance again. sendingAvailable describes channel and provider configuration, not quota availability. An idempotency key is only required for sending. ## Track message and batch delivery ~~~bash # Replace BATCH_ID and MESSAGE_ID with IDs from your send response. curl "https://nisend.pakismart.co.tz/v1/api/batches/BATCH_ID?page=1&pageSize=25" -H "Authorization: Bearer $NISEND_API_KEY" curl "https://nisend.pakismart.co.tz/v1/api/messages/MESSAGE_ID" -H "Authorization: Bearer $NISEND_API_KEY" ~~~ Batch responses include summary counts for the entire batch, complete and paginated messages. complete becomes true when all recipients are delivered or failed. Pagination fields are total, page, pageSize and pages; pageSize accepts 25 (default), 50 or 100. Message lookup returns a message object with id, batchId, to, sender, text, status, errorCode, createdAt, submittedAt and deliveredAt. Batch messages use the same fields. Poll from your server a few seconds apart and stop at a final status. A lost or delayed delivery report can leave a message SENT. Delivery webhooks are not available yet. | Status | Meaning | | --- | --- | | QUEUED | Waiting for submission | | SENT | Submitted to the provider; awaiting final delivery outcome | | DELIVERED | Delivery confirmed; final | | FAILED | Final failure | ## Endpoint reference | Method | Endpoint | Permission | | --- | --- | --- | | GET | /api/balance | messaging:read | | GET | /api/senders | messaging:read | | POST | /api/messages/estimate | messaging:read | | POST | /api/messages/sms | messaging:send | | POST | /api/messages/sms/bulk | messaging:send | | GET | /api/messages/:messageId | messaging:read | | GET | /api/batches/:batchId | messaging:read | | GET | /api/messages | messaging:read | | GET | /api/workspace | workspace:read | | GET | /api/messaging/capabilities | messaging:read | GET /api/messages returns the latest 50 messages. Use ?cursor=MESSAGE_ID for older messages. ## Delivery and retries Messages move from QUEUED to SENT, then DELIVERED or FAILED. Use a unique idempotencyKey (8–100 letters, numbers, underscores or hyphens) per intended message or batch. Retry the same payload with the same key after a timeout to avoid duplicate sends. Different content with the same key returns 409. Phone numbers accept international format or local Tanzanian numbers. Text is limited to 1,600 characters. Line breaks count too. Long or Unicode messages can use multiple SMS credits per recipient. Use the estimate endpoint before bulk sending. ## API key management Give keys meaningful names. Rename, rotate or revoke them from [API keys](https://messaging.pakismart.co.tz/login). Secrets are shown once. Rotation immediately invalidates the old secret, retaining permissions and expiry. Revocation is permanent. Archived workspaces have their keys revoked. Create a new key if an older read-only key needs SMS sending. ## Errors | HTTP status | Meaning | | --- | --- | | 400 | Invalid request | | 401 | Invalid, expired or revoked API key | | 402 | Insufficient SMS credits | | 403 | Permission, sender or workspace restriction | | 404 | Message or batch not found in this workspace | | 409 | Conflicting request or missing default sender | | 429 | Rate or queue limit; retry later | | 503 | Sending temporarily unavailable | # Endpoint reference ## Send an SMS POST /api/messages/sms Required permission: messaging:send Queue one SMS. A 202 response means accepted into the queue, not delivered. Replace example phone numbers and sender names before sending. Parameters: - to (string, required): One international or local Tanzanian phone number. - text (string, required): Message text, 1–1,600 characters. Line breaks count. Long or Unicode text can require multiple SMS credits. - senderId (string, optional): Your approved sender NAME, for example PAX CARGO. Omit only if a platform default is configured. Do not invent a sender name. - idempotencyKey (string, required): 8–100 letters, digits, underscores or hyphens. Use one key per intended send; reuse the same key and payload after a timeout. Request JSON: ```json { "to": "255712345678", "text": "Hello from Nisend!", "senderId": "PAX CARGO", "idempotencyKey": "order_123_confirmation" } ``` Example response (HTTP 202): ```json { "message": { "id": "msg_example", "workspaceId": "workspace_example", "sender": "PAX CARGO", "to": "255712345678", "text": "Hello from Nisend!", "status": "QUEUED", "providerStatus": null, "errorCode": null, "createdAt": "2026-01-01T10:00:00.000Z", "dispatchStartedAt": null, "providerDurationMs": null, "submittedAt": null, "deliveredAt": null } } ``` ## Send bulk SMS POST /api/messages/sms/bulk Required permission: messaging:send Send the same text to up to 5,000 unique recipients. Numbers from all sources are normalized and deduplicated. Credits are reserved atomically; invalid recipients or insufficient credits reject the whole batch. Retry with the same key and payload to retrieve the original batch, even if group membership changes. Parameters: - to (string[], optional): International or local Tanzanian phone numbers. Combine with contactIds or groupIds. At least one recipient source is required; maximum 5,000 unique recipients. - contactIds (string[], optional): Saved contact IDs belonging to this workspace; maximum 5,000. - groupIds (string[], optional): Contact group IDs belonging to this workspace; maximum 200. - text (string, required): Message text, 1–1,600 characters. Line breaks count. Long or Unicode text can require multiple SMS credits. - senderId (string, optional): Your approved sender NAME, for example PAX CARGO. Omit only if a platform default is configured. Do not invent a sender name. - idempotencyKey (string, required): 8–100 letters, digits, underscores or hyphens. Use one key per intended send; reuse the same key and payload after a timeout. Request JSON: ```json { "to": [ "255712345678", "255712345679" ], "text": "Your shipment is ready.", "senderId": "PAX CARGO", "idempotencyKey": "shipment_batch_001" } ``` Example response (HTTP 202): ```json { "batchId": "batch_example", "recipientCount": 2, "credits": 2, "trackingUrl": "/v1/api/batches/batch_example" } ``` ## Estimate SMS credits POST /api/messages/estimate Required permission: messaging:read Preview recipient count and SMS segment credits without sending or reserving anything. This is not a TZS price. Sending checks the current balance and recipients again. sendingAvailable describes channel/provider configuration, not quota availability. Parameters: - to (string[], optional): International or local Tanzanian phone numbers. Combine with contactIds or groupIds. At least one recipient source is required; maximum 5,000 unique recipients. - contactIds (string[], optional): Saved contact IDs belonging to this workspace; maximum 5,000. - groupIds (string[], optional): Contact group IDs belonging to this workspace; maximum 200. - text (string, required): Message text, 1–1,600 characters. Line breaks count. Long or Unicode text can require multiple SMS credits. - senderId (string, optional): Your approved sender NAME, for example PAX CARGO. Omit only if a platform default is configured. Do not invent a sender name. Request JSON: ```json { "to": [ "255712345678", "255712345679" ], "text": "Your shipment is ready.", "senderId": "PAX CARGO" } ``` Example response (HTTP 200): ```json { "recipientCount": 2, "segmentsPerMessage": 1, "credits": 2, "balance": 100, "sufficientBalance": true, "sender": "PAX CARGO", "sendingAvailable": true } ``` ## Get SMS balance GET /api/balance Required permission: messaging:read Read available SMS segment credits after reservations. This is not a cash balance, and a long message can require more than one credit. No request body. Example response (HTTP 200): ```json { "workspaceId": "workspace_example", "balance": 100, "unit": "SMS segments" } ``` ## List approved senders GET /api/senders Required permission: messaging:read List approved sender names for this workspace. Pending and suspended senders are excluded. Use the returned senderId name in send requests. The platform default is returned separately. No request body. Example response (HTTP 200): ```json { "senders": [ { "senderId": "PAX CARGO", "status": "APPROVED" } ], "defaultSender": null } ``` ## Get message status GET /api/messages/:messageId Required permission: messaging:read Read a message belonging to the key’s workspace. Statuses are QUEUED, SENT, DELIVERED and FAILED. Poll a few seconds apart and stop at DELIVERED or FAILED. Delivery webhooks are not available yet. Parameters: - messageId (string, required, path): The message ID returned by sending or batch tracking. No request body. Example response (HTTP 200): ```json { "message": { "id": "msg_example", "batchId": null, "to": "255712345678", "sender": "PAX CARGO", "text": "Hello from Nisend!", "status": "DELIVERED", "errorCode": null, "createdAt": "2026-01-01T10:00:00.000Z", "submittedAt": "2026-01-01T10:00:01.000Z", "deliveredAt": "2026-01-01T10:00:03.000Z" } } ``` ## Get batch delivery GET /api/batches/:batchId Required permission: messaging:read Read full-batch delivery counts and one page of messages. complete is true only after all recipients reach DELIVERED or FAILED. A delayed report can leave a message SENT. Parameters: - batchId (string, required, path): The batch ID returned by bulk sending. - page (integer, optional, query): Page number, 1–100,000; default 1. Pages beyond the last page are clamped. - pageSize (integer, optional, query): 25 (default), 50 or 100. No request body. Example response (HTTP 200): ```json { "batchId": "batch_example", "recipientCount": 2, "credits": 2, "createdAt": "2026-01-01T10:00:00.000Z", "summary": { "QUEUED": 0, "SENT": 0, "DELIVERED": 2, "FAILED": 0 }, "complete": true, "total": 2, "page": 1, "pageSize": 25, "pages": 1, "messages": [ { "id": "msg_example", "batchId": "batch_example", "to": "255712345678", "sender": "PAX CARGO", "text": "Your shipment is ready.", "status": "DELIVERED", "errorCode": null, "createdAt": "2026-01-01T10:00:00.000Z", "submittedAt": "2026-01-01T10:00:01.000Z", "deliveredAt": "2026-01-01T10:00:03.000Z" }, { "id": "msg_example_2", "batchId": "batch_example", "to": "255712345679", "sender": "PAX CARGO", "text": "Your shipment is ready.", "status": "DELIVERED", "errorCode": null, "createdAt": "2026-01-01T10:00:00.000Z", "submittedAt": "2026-01-01T10:00:01.000Z", "deliveredAt": "2026-01-01T10:00:03.000Z" } ] } ``` ## List messages GET /api/messages Required permission: messaging:read Read the latest 50 messages. For the next page, set cursor to the last message ID from the previous page. An empty messages array means there are no more results. Parameters: - cursor (string, optional, query): Last message ID from the previous page, for example ?cursor=msg_example. No request body. Example response (HTTP 200): ```json { "messages": [ { "id": "msg_example", "workspaceId": "workspace_example", "sender": "PAX CARGO", "to": "255712345678", "text": "Hello from Nisend!", "status": "DELIVERED", "providerStatus": null, "errorCode": null, "createdAt": "2026-01-01T10:00:00.000Z", "dispatchStartedAt": "2026-01-01T10:00:00.500Z", "providerDurationMs": 500, "submittedAt": "2026-01-01T10:00:01.000Z", "deliveredAt": "2026-01-01T10:00:03.000Z" } ] } ``` ## Get workspace GET /api/workspace Required permission: workspace:read Read the public configuration of the workspace linked to your API key. Every key belongs to one workspace. No request body. Example response (HTTP 200): ```json { "workspace": { "id": "workspace_example", "name": "Production", "businessName": "Example business", "status": "ACTIVE", "channels": [ "SMS" ], "timeZone": "Africa/Dar_es_Salaam" } } ``` ## Get capabilities GET /api/messaging/capabilities Required permission: messaging:read Read selected channels and platform sending configuration. sendingEnabled reflects channel/provider/default-sender configuration, not remaining credits or quotas. Use the estimate endpoint to check your selected sender and required credits. No request body. Example response (HTTP 200): ```json { "workspaceId": "workspace_example", "selectedChannels": [ "SMS" ], "sendingEnabled": true } ``` # Errors 400: Invalid request. Check the message, recipient format and required fields. 401: Missing, invalid, expired or revoked API key. 402: Insufficient SMS credits. 403: Missing key permission, unapproved sender or restricted workspace. 404: Message or batch not found in this workspace. 409: Reused idempotency key with a different payload, or missing default sender. 429: Rate, queue or daily limit reached. Back off before retrying the same request. 503: Sending temporarily unavailable. Check message status before retrying an uncertain send.