SendAfrica logoSendAfricaDocs

Message Logs

Query paginated message history with filters for status, recipient, and date range to reconcile delivery.


#List message logs

GET/v1/sms/logsJWT
ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger25Results per page
statusstringFilter: sent, delivered, failed, …
tostringFilter by recipient number
from_date / to_datedateDate range filter
bash
bash
curl -s "https://api.sendafrica.online/v1/sms/logs?page=1&per_page=10&status=delivered" \
  -H "Authorization: Bearer $JWT_TOKEN"
200 OK
json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "SA-f3b1c2d49e8a4f2bb1c2d3e4f5a6b7c8",
        "recipient": "+255712345678",
        "message": "Your OTP is 482910...",
        "sender": "MyBrand",
        "status": "delivered",
        "credits_used": 1,
        "campaign_id": null,
        "created_at": "2026-06-11T16:24:05Z",
        "delivered_at": "2026-06-11T16:24:09Z"
      }
    ],
    "total": 143,
    "page": 1,
    "per_page": 10,
    "total_pages": 15
  }
}

#Delivery lifecycle

The send call is synchronous — by the time you receive a response, the message has already been submitted to the gateway. Delivery updates arrive later via webhook.

statuses
text
send response: sent ──▶ delivered
   (or failed)     ├──▶ pending ──▶ delivered
                   │        └──▶ failed
                   └──▶ failed
  • sent — accepted by the gateway; the immediate send response for a successful submit.
  • pending — the carrier buffered or queued the message; it may still be delivered.
  • delivered — handset confirmed receipt, via the delivery webhook.
  • failed — rejected at submit time, rejected/expired at the carrier, or undelivered; credits were already refunded at gateway-rejection time where applicable.

Webhooks vs polling

Polling logs works, but webhooks give you sub-second delivery confirmation without burning rate limit. Use polling as a fallback reconciliation job (e.g. hourly), not as your primary signal.

#Reconciliation pattern

reconcile.ts
typescript
// Hourly job: find messages stuck in "sent" older than 10 minutes
// and refresh their state from the log.
async function reconcileStuckMessages(jwt: string) {
  const cutoff = new Date(Date.now() - 10 * 60_000).toISOString();

  const res = await fetch(
    "https://api.sendafrica.online/v1/sms/logs?status=sent&per_page=200",
    { headers: { Authorization: `Bearer ${jwt}` } },
  );
  const { data } = await res.json();

  for (const msg of data.items) {
    if (new Date(msg.created_at) < new Date(cutoff)) {
      await escalate(msg.id); // alert / re-check / mark unknown
    }
  }
}