Skip to content

Check message status

GET /status/{message_id} returns the current state of a previously- submitted SMS. The path parameter is the message_id returned by POST /send.

Terminal window
curl $BASE_URL/api/v1/lisoloo/sms-api/status/507f1f77bcf86cd799439011 \
-H "app-key: $LISOLOO_API_KEY"
{
"status_code": 200,
"data": {
"message_id": "507f1f77bcf86cd799439011",
"status": "delivered",
"total_recipients": 1,
"delivered": 1,
"failed": 0,
"pending": 0,
"created_at": "2026-05-27T10:15:00Z",
"updated_at": "2026-05-27T10:15:08Z"
}
}
FieldDescription
statusAggregate status across all recipients. See Message lifecycle for the full enum.
total_recipientsNumber of recipients in the original to array (from OPS_SMS_MESSAGE.total_recipients).
delivered / failed / pendingPer-state counts derived from the underlying OPS_SMS_MESSAGE_CONTACT rows. pending includes contacts in any non-terminal state.
created_atWhen POST /send returned.
updated_atLast state-change timestamp.

The right cadence depends on traffic shape. For interactive flows (OTP, single-recipient confirmations), poll 3 times then give up:

import time
import requests
def wait_for_delivery(message_id, max_attempts=3, delay=2):
for _ in range(max_attempts):
r = requests.get(f"{API_URL}/status/{message_id}", headers=HEADERS)
data = r.json()["data"]
if data["status"] in ("delivered", "failed"):
return data
time.sleep(delay)
return data # last-seen state

For batch flows, switch to webhooks instead — polling 1 000 message_ids burns rate-limit budget.

HTTP/1.1 404 Not Found
{
"detail": "Message not found"
}

Returned when:

  • The message_id doesn’t exist.
  • The message_id belongs to a different merchant — the lookup is scoped to your sys_organization_id, so cross-tenant IDs surface as 404 and never leak existence.

A multi-recipient send can finish with some recipients delivered and others failed. The aggregate status field reflects the message- level state machine (see Message lifecycle); the delivered and failed counters give you the exact split.