Skip to content
Sendozi

Getting started

Quickstart: send your first SMS

Create an account, take a sk_test_ key, POST to /v1/sms/send with a sender, a +234 recipient, a message and an sms_type, then read the success envelope. Sandbox runs the same validation without calling a provider or debiting the wallet.

By SendoziUpdated 3 min read

Before you start

  • A Sendozi account. Email and phone verification are part of signup.
  • An API key from the Console. Sandbox keys start sk_test_, production keys sk_live_.
  • A recipient number in +234 format. The API does not accept local 0-prefixed numbers.
  • For production only: KYC approval and an approved sender ID.

1. Get an API key

Create a key in the Console under API keys. The secret is shown once and stored as a SHA-256 hash - a lost key is replaced, not recovered. Put it in an environment variable; never in source control.

Keep the key out of your code
Shell
export SENDOZI_API_KEY="sk_test_your_sandbox_key"
export SENDOZI_SENDER_ID="Sendozi"

2. Send your first message

POST /v1/sms/send
cURL
curl -X POST https://api.sendozi.com/v1/sms/send \
  -H "Authorization: Bearer $SENDOZI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-001" \
  -d '{
    "sender": "Sendozi",
    "recipient": "+2348012345678",
    "message": "Your Acme code is 492811. It expires in 10 minutes.",
    "sms_type": "transactional"
  }'
Node.js
const response = await fetch("https://api.sendozi.com/v1/sms/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SENDOZI_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "quickstart-001",
  },
  body: JSON.stringify({
    sender: process.env.SENDOZI_SENDER_ID,
    recipient: "+2348012345678",
    message: "Your Acme code is 492811. It expires in 10 minutes.",
    sms_type: "transactional",
  }),
});

const body = await response.json();
if (!body.success) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data);
Python
import os
import httpx

response = httpx.post(
    "https://api.sendozi.com/v1/sms/send",
    headers={
        "Authorization": f"Bearer {os.environ['SENDOZI_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "quickstart-001",
    },
    json={
        "sender": os.environ["SENDOZI_SENDER_ID"],
        "recipient": "+2348012345678",
        "message": "Your Acme code is 492811. It expires in 10 minutes.",
        "sms_type": "transactional",
    },
    timeout=15.0,
)

body = response.json()
if not body["success"]:
    raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
print(body["data"])

3. Read the response

Every response, success or failure, uses the same envelope. Write your client against the envelope once and every endpoint behaves the same way.

Success and failure share a shape
Success
{
  "success": true,
  "data": {
    "message_id": "msg_1f4c0a2b",
    "channel": "sms",
    "mode": "sandbox",
    "status": "accepted",
    "recipient": "+2348012345678",
    "customer_price_kobo": 700
  },
  "request_id": "req_8ecdcb4ce2ae4290"
}
Failure
{
  "success": false,
  "error": {
    "code": "sender_id_not_approved",
    "message": "The sender ID is not approved for this workspace.",
    "resolution": "Use an approved sender ID, or submit this one for registration."
  },
  "request_id": "req_8ecdcb4ce2ae4290"
}
  • status accepted means the request was validated and queued. It is not delivery.
  • request_id is also returned as the x-request-id header. Log it - it is how support traces a send.
  • Every error carries a resolution written for a developer to act on, not for display to an end user.
  • customer_price_kobo is what the send will cost. Sandbox reports it without debiting.

4. Find out what happened to it

Accepted is the start of the story. Register a webhook endpoint so Sendozi pushes delivery events to you rather than making you poll.

Register an endpoint, then test it
cURL
curl -X POST https://api.sendozi.com/v1/webhooks/endpoints \
  -H "Authorization: Bearer $SENDOZI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/hooks/sendozi",
    "events": ["message.delivered", "message.failed"]
  }'

You can also read status directly: GET /v1/messages/{id} for one message, GET /v1/messages for a paginated list. See delivery webhooks for the receiver pattern.

5. Move to production

  1. 1

    Complete KYC in the Console

    Production access is gated on it. Until it is approved, live sends return kyc_required.

  2. 2

    Register a sender ID

    A live send with an unapproved sender returns sender_id_not_approved. Registration depends on the networks, so start early.

  3. 3

    Fund the wallet

    Sends are debited per page. An empty wallet returns insufficient_balance before anything is sent.

  4. 4

    Swap the key

    Change sk_test_ to sk_live_ in your environment. Nothing else in your code changes.

  5. 5

    Send one real message to your own phone

    Then check the delivery report and confirm your webhook fired.

A production send is checked through thirteen gates in a fixed order, and the first failure is returned without charging you. The error reference lists the order and what each code means.

Frequently asked questions

Do I need an SDK to use the Sendozi API?
No, and there is no official SDK to install. Every endpoint is a plain HTTPS request with a JSON body, so the HTTP client already in your project is enough.
Does a sandbox send cost anything?
No. A sk_test_ key records the send, calls no provider and debits nothing. It still runs recipient validation, content policy checks and suppression checks, so a request that fails in sandbox would have failed live.
Why is my first live send rejected?
Usually one of three gates: KYC not approved (kyc_required), the sender ID not registered (sender_id_not_approved), or an empty wallet (insufficient_balance). The resolution field on the error names the fix.
What is the base URL?
https://api.sendozi.com. GET /v1 returns a live index of every available route.

Get a sandbox key

Create an account and take a sk_test_ key. Sandbox sends validate exactly like live ones and cost nothing.