UC-004 — Check Delivery Status
| Field | Value |
|---|---|
| ID | UC-004 |
| Goal | Check message delivery status with polling and webhooks |
| Channel | Multi-channel (SMS, RCS, WhatsApp) |
| Complexity | Basic |
| Estimated time | 15 minutes |
| APIs involved | GET /api/partner-gateway/v1/messages/status/{customerMessageId}, GET /api/partner-gateway/v1/messages/status |
Real-world scenarios
- OTP dashboard — Send verification: BancaSicura monitors in real time whether OTP codes are delivered within 5 seconds, activating an alternative channel in case of failure.
- Campaign delivery report: MarketingPro generates an end-of-campaign SMS report with delivery percentages, errors, and average times.
- Real-time monitoring: TechStore integrates the status check into their CRM to show a "Delivered" or "Pending" badge next to each sent communication.
Prerequisites
Before you begin, make sure you have:
- Active API Key → How to get one
- Sufficient credit → Check in the Qlara Dashboard
- A previously sent message with its
messageId
:::tip Test without costs
Add "simulation": true in the request body to validate the flow without actually sending messages and without consuming credit.
:::
Polling vs Webhook
| Feature | Polling | Webhook |
|---|---|---|
| Direction | Your server calls the API | The API calls your server |
| Latency | Depends on polling interval | Real time |
| API load | Proportional to the number of calls | One call per event |
| Complexity | Low (just a loop) | Medium (requires a public endpoint) |
| Ideal for | Spot checks, debug, small volumes | Production, high volumes, real-time |
:::tip Which one to choose? Use polling for tests, debugging, and manual checks. Use webhooks in production to receive real-time notifications without overloading the API. See the Webhook guide for configuration. :::
Step 1 — Send a message (prerequisite)
To check the status, you must first have sent a message and saved the messageId. Quick example with SMS:
curl -s -X POST https://lora-api.agiletelecom.com/api/message-server/sms/send \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"destination": "+393471234567",
"sender": "BancaSicura",
"body": "Il tuo codice OTP e: 847293. Valido per 5 minuti.",
"enableNotification": true
}'
Response
{
"messageId": "d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890",
"simulation": false,
"results": {
"sms": {
"accepted": true,
"unicode": false,
"parts": 1,
"reasons": []
}
}
}
Behind the scenes — Status timeline
After sending, the message goes through these states in sequence:
- SENT — The gateway accepted the message and forwarded it to the carrier.
- DELIVERED — The carrier confirms delivery to the recipient's device.
- READ (RCS/WhatsApp only) — The recipient has opened and viewed the message.
- ERROR — The carrier reports a delivery error.
- EXPIRED — The message was not delivered within the TTL (Time To Live).
The SENT -> DELIVERED transition typically takes 1-5 seconds for SMS, 0.5-2 seconds for RCS/WhatsApp.
Step 2 — Single polling
Query the status of a single message using the messageId as a path parameter and the channel as a query parameter.
curl -X GET "https://lora-api.agiletelecom.com/api/partner-gateway/v1/messages/status/d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890?channel=SMS" \
-H "X-Api-Key: YOUR_API_KEY"
Response — DELIVERED
{
"customerMessageId": "d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890",
"channel": "SMS",
"destination": "+393471234567",
"deliveryStatus": "DELIVERED",
"deliveryStatusDescription": "Message delivered to handset",
"sendDate": "2026-04-09T15:00:00+02:00",
"deliveryDate": "2026-04-09T15:00:03+02:00",
"readDate": null
}
Response — ERROR
{
"customerMessageId": "d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890",
"channel": "SMS",
"destination": "+393471234567",
"deliveryStatus": "ERROR",
"deliveryStatusDescription": "Undeliverable",
"sendDate": "2026-04-09T15:00:00+02:00",
"deliveryDate": null,
"readDate": null
}
Behind the scenes — Complete status table
| Status | Webhook code | Channel | Description |
|---|---|---|---|
DELIVERED | 3 | SMS, RCS, WhatsApp | Message delivered successfully |
SENT | — | All | Message sent, waiting for carrier confirmation |
RECEIVED | — | RCS, WhatsApp | Message received by the WhatsApp/RCS server |
ERROR | 6 | All | Delivery error (non-existent number, unreachable network) |
EXPIRED | 8 | All | TTL expired, the message was not delivered in time |
UNKNOWN | — | All | Status not yet determined |
Note on webhook codes: The numeric statusCode values (3, 6, 8) are used in webhook callbacks. In the status polling API, the deliveryStatus field is a human-readable string.
Step 3 — Batch polling
To check the status of multiple messages in a single call, use the batch endpoint. All IDs must belong to the same channel.
curl -X GET "https://lora-api.agiletelecom.com/api/partner-gateway/v1/messages/status?channel=SMS&ids=d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890,a1b2c3d4-e5f6-7890-abcd-ef1234567890,f47ac10b-58cc-4372-a567-0e02b2c3d479" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Batch status
[
{
"customerMessageId": "d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890",
"channel": "SMS",
"destination": "+393471234567",
"deliveryStatus": "DELIVERED",
"deliveryStatusDescription": "Message delivered",
"sendDate": "2026-04-09T15:00:00+02:00",
"deliveryDate": "2026-04-09T15:00:03+02:00",
"readDate": null
},
{
"customerMessageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"channel": "SMS",
"destination": "+393481234567",
"deliveryStatus": "DELIVERED",
"deliveryStatusDescription": "Message delivered",
"sendDate": "2026-04-09T15:00:01+02:00",
"deliveryDate": "2026-04-09T15:00:04+02:00",
"readDate": null
},
{
"customerMessageId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"channel": "SMS",
"destination": "+393491234567",
"deliveryStatus": "ERROR",
"deliveryStatusDescription": "Undeliverable",
"sendDate": "2026-04-09T15:00:01+02:00",
"deliveryDate": null,
"readDate": null
}
]
:::note IDs not found IDs that do not match any message are silently omitted from the response. If you send 3 IDs and receive only 2 results, the third was not found. :::
Behind the scenes — Batch limits and best practices
- ID limit: You can send up to several hundred IDs in a single call. For higher volumes, split into multiple requests.
- Same channel: All IDs must belong to the same channel (SMS, RCS, or WHATSAPP). For mixed channels, make separate calls.
- Result order: Results are not ordered. Use
customerMessageIdto match with your records. - Polling interval: For automated checks, use an interval of at least 5 seconds between calls. For high volumes, switch to webhooks.
Example: polling loop with retry
A bash script that checks the status every 3 seconds, with a 30-second timeout:
#!/bin/bash
MESSAGE_ID="d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890"
API_KEY="YOUR_API_KEY"
MAX_ATTEMPTS=10
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
ATTEMPT=$((ATTEMPT + 1))
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS..."
STATUS=$(curl -s -X GET \
"https://lora-api.agiletelecom.com/api/partner-gateway/v1/messages/status/${MESSAGE_ID}?channel=SMS" \
-H "X-Api-Key: ${API_KEY}" | jq -r '.deliveryStatus')
echo "Status: $STATUS"
if [ "$STATUS" = "DELIVERED" ]; then
echo "Message delivered!"
exit 0
elif [ "$STATUS" = "ERROR" ] || [ "$STATUS" = "EXPIRED" ]; then
echo "Delivery error: $STATUS"
exit 1
fi
sleep 3
done
echo "Timeout: final status not reached"
exit 2
Channel comparison
| Field | SMS | RCS | |
|---|---|---|---|
deliveryStatus | DELIVERED, SENT, ERROR, EXPIRED | DELIVERED, SENT, ERROR, EXPIRED | DELIVERED, SENT, RECEIVED, ERROR, EXPIRED |
readDate | Always null | Present if read | Present if read |
| Typical DELIVERED latency | 1-5 seconds | 0.5-2 seconds | 0.5-2 seconds |
| READ webhook | Not available | Available | Available |
Common errors
404 — Message not found
{}
Solution: Verify that the customerMessageId is correct and that the channel parameter matches the channel used for sending. Very old messages may no longer be available.
Wrong channel in channel parameter
If you send a message via SMS but query the status with channel=RCS, you will get a 404 even if the message exists. Make sure the channel in the query matches the one used for sending.
Persistent UNKNOWN status
If the status remains UNKNOWN for more than 60 seconds, it may indicate a routing problem. Check:
- The recipient's number is valid and reachable
- The recipient's carrier is supported
- There are no ongoing network issues
Expected result
| Step | Action | Result |
|---|---|---|
| 1 | POST /sms/send (or RCS/WhatsApp) | messageId saved |
| 2 | GET /messages/status/{id}?channel=SMS | deliveryStatus: "DELIVERED" |
| 3 | GET /messages/status?channel=SMS&ids=id1,id2,id3 | Array of statuses for each message |
Next steps
- UC-001 — Send Single SMS: Complete SMS send and verify scenario
- UC-002 — Send RCS Rich Card: Rich card with read receipt
- UC-003 — Send WhatsApp Template: Template with status tracking
- Webhook Guide: Configure webhooks for real-time notifications
- Authentication Guide: API Key and Basic Auth setup