SMS API

Developer-first SMS API for Ghana

One REST API for transactional SMS, bulk sends, and OTP verification. Bearer keys, idempotent POST, inspectable delivery reports, and signed webhooks — the same pipeline the dashboard uses.

Quick start

Send your first message

  1. 01Create an account and copy a TEST key. It is shown once.
  2. 02POST /v1/messages with a Bearer key and an Idempotency-Key.
  3. 03Open the message id. Accepted is not the same as delivered.
export API_URL=https://api.taysend.com
export KEY=sms_test_your_key

curl -X POST "$API_URL/v1/messages" \
  -H "Authorization: Bearer $KEY" \
  -H "Idempotency-Key: 11111111-1111-1111-1111-111111111111" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+233200000001",
    "sender_id": "TAYSEND",
    "message": "Hello from Taysend",
    "type": "transactional"
  }'

Try it in the sandbox

Sends a simulated TEST message. No API key and no real SMS.

Sandbox

TEST

This number ends as delivered.

Response

{
  "id": "msg_…",
  "status": "QUEUED",
  "environment": "TEST"
}

Reference

What the API covers

One REST surface for send, OTP, delivery, and webhooks. The dashboard calls the same paths.

Bearer sms_test_

Authentication

Bearer API keys prefixed sms_test_ or sms_live_. Keys are shown once at creation. TEST keys cannot send LIVE traffic.

POST /v1/messages

Send single or bulk

POST /v1/messages accepts to for one recipient or recipients for bulk. Include sender_id, message or templateId, and type (transactional or marketing).

POST /v1/verify/send

OTP verification

POST /v1/verify/send and POST /v1/verify/check handle codes on a dedicated queue. Codes are hashed at rest; TEST never hits a real handset.

GET /v1/messages/:id

Delivery reports

GET /v1/messages/:id returns status, segments, encoding, and failure reason. HTTP 200 on send means accepted — delivery is a later DLR state.

t= · v1=

Webhooks

Signed payloads with t= timestamp and v1= HMAC signature. Events include message.delivered, message.failed, campaign.completed, and wallet.low_balance.

Idempotency-Key

Idempotency

Send Idempotency-Key with POST /v1/messages. Replay the same key and body for the original response. A different body with the same key returns a conflict.

Simulated numbers

TEST vs LIVE

TEST uses simulated numbers with known outcomes (+233200000001 delivers, +233200000002 fails, etc.). LIVE requires an approved sender ID.

INSUFFICIENT_BALANCE

Errors and rate limits

Structured error envelope with codes like INSUFFICIENT_BALANCE. Rate limit headers document remaining quota — see /developers/errors and /developers/rate-limits.

Routing

Encoding and Ghana routing

Network is inferred from the recipient prefix: MTN, Telecel, or AT. Encoding is computed from the message body. GSM-7 allows 160 characters per segment. UCS-2 (Unicode) uses 70. The API returns segment count on acceptance so you can reconcile wallet charges.

GSM-7 160 · Unicode 70 · prefix picks the network

POST /v1/messages

{
  "to": "+233200000001",
  "sender_id": "TAYSEND",
  "message": "Hello from Taysend",
  "type": "transactional"
}

FAQ

Straight answers

If something here is product behaviour, it matches the dashboard and the API.

01What is the API base URL?+

Production API host is https://api.taysend.com. Copy-paste examples in the docs use this host.

02How do I send my first message?+

Create a TEST key at /register, then POST /v1/messages with Authorization: Bearer sms_test_..., an Idempotency-Key header, and a JSON body with to, sender_id, and message.

03Can I send bulk SMS through the API?+

Yes. Pass a recipients array instead of to. Bulk responses include a count. Campaign snapshot logic applies when sending from the dashboard.

04How do webhooks verify authenticity?+

Each webhook includes t= (timestamp) and v1= (HMAC signature). Verify the signature before trusting the payload. Full reference at /developers/webhooks.

Start building

Create a TEST key and hit a simulated number today.