Skip to main content

UC-004 — Check Delivery Status

FieldValue
IDUC-004
GoalCheck message delivery status with polling and webhooks
ChannelMulti-channel (SMS, RCS, WhatsApp)
ComplexityBasic
Estimated time15 minutes
APIs involvedGET /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:

:::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​

FeaturePollingWebhook
DirectionYour server calls the APIThe API calls your server
LatencyDepends on polling intervalReal time
API loadProportional to the number of callsOne call per event
ComplexityLow (just a loop)Medium (requires a public endpoint)
Ideal forSpot checks, debug, small volumesProduction, 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:

  1. SENT — The gateway accepted the message and forwarded it to the carrier.
  2. DELIVERED — The carrier confirms delivery to the recipient's device.
  3. READ (RCS/WhatsApp only) — The recipient has opened and viewed the message.
  4. ERROR — The carrier reports a delivery error.
  5. 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
StatusWebhook codeChannelDescription
DELIVERED3SMS, RCS, WhatsAppMessage delivered successfully
SENT—AllMessage sent, waiting for carrier confirmation
RECEIVED—RCS, WhatsAppMessage received by the WhatsApp/RCS server
ERROR6AllDelivery error (non-existent number, unreachable network)
EXPIRED8AllTTL expired, the message was not delivered in time
UNKNOWN—AllStatus 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 customerMessageId to 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​

FieldSMSRCSWhatsApp
deliveryStatusDELIVERED, SENT, ERROR, EXPIREDDELIVERED, SENT, ERROR, EXPIREDDELIVERED, SENT, RECEIVED, ERROR, EXPIRED
readDateAlways nullPresent if readPresent if read
Typical DELIVERED latency1-5 seconds0.5-2 seconds0.5-2 seconds
READ webhookNot availableAvailableAvailable

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​

StepActionResult
1POST /sms/send (or RCS/WhatsApp)messageId saved
2GET /messages/status/{id}?channel=SMSdeliveryStatus: "DELIVERED"
3GET /messages/status?channel=SMS&ids=id1,id2,id3Array of statuses for each message

Next steps​