Webhooks and Delivery Notifications
There are two ways to track message delivery: polling and webhooks. Choose the approach that best fits your use case.
Polling
Query the delivery status on demand:
GET /messages/status/{customerMessageId}
Or check multiple messages at once:
GET /messages/status?customerMessageIds=id1,id2,id3
When to use polling:
- Low message volume
- Ad-hoc status checks
- Simple integrations that don't need a public endpoint
Webhooks
Register an HTTPS callback URL to receive delivery status updates in real time, as soon as the carrier reports them.
Configure a webhook
POST /webhooks/delivery-status
Provide your callback URL. The API will send HTTP POST requests to this URL whenever a message status changes.
Manage your webhook
| Action | Endpoint |
|---|---|
| Get current webhook | GET /webhooks/delivery-status |
| Create webhook | POST /webhooks/delivery-status |
| Update webhook URL | PUT /webhooks/delivery-status |
| Revoke webhook | DELETE /webhooks/delivery-status |
Only one active delivery-status webhook is allowed per account. Creating a new one replaces the previous configuration.
Webhook flow
Webhook payload
When a message status changes, the API sends a POST request to your callback URL.
Delivery notification example:
{
"eventType": "DELIVERY",
"messageId": "e76614d1-4ac1-4d94-89f0-d07f1b5a190c",
"customerMessageId": "order-12345",
"destination": "+393401234567",
"statusCode": 3,
"description": "Message delivered",
"channel": "SMS",
"timestamp": "2026-04-09T10:30:00Z"
}
Read notification example (WhatsApp/RCS only):
{
"eventType": "READ",
"messageId": "e76614d1-4ac1-4d94-89f0-d07f1b5a190c",
"destination": "+393401234567",
"channel": "WHATSAPP",
"timestamp": "2026-04-09T10:31:15Z"
}
Inbound message example:
{
"eventType": "INBOUND",
"source": "+393401234567",
"destination": "+393409876543",
"text": "Yes, I confirm my appointment",
"channel": "WHATSAPP",
"messageType": "TEXT",
"timestamp": "2026-04-09T10:32:00Z"
}
| Status Code | Meaning |
|---|---|
| 3 | Delivered |
| 6 | Undeliverable |
| 8 | Expired |
Testing webhooks locally
To test webhooks during development, use ngrok to expose your local server:
# Start your local webhook handler on port 3000
node webhook-handler.js
# In another terminal, expose it via ngrok
ngrok http 3000
# Copy the HTTPS URL (e.g. https://abc123.ngrok.io)
# Register it as your webhook callback:
curl -X POST "https://lora-api.agiletelecom.com/api/partner-gateway/v1/webhooks/delivery-status" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"callbackUrl": "https://abc123.ngrok.io/webhook"}'
Use simulation: true when sending test messages — the message won't be delivered but you'll still receive webhook callbacks.
Example webhook handler (Node.js)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const { eventType, messageId, statusCode, channel } = req.body;
switch (eventType) {
case 'DELIVERY':
if (statusCode === 3) {
console.log(`Message ${messageId} delivered via ${channel}`);
// Update your database: mark message as delivered
} else if (statusCode === 6) {
console.log(`Message ${messageId} undeliverable via ${channel}`);
// Handle failure: retry or notify user
}
break;
case 'READ':
console.log(`Message ${messageId} read by recipient`);
break;
case 'INBOUND':
console.log(`Inbound from ${req.body.source}: ${req.body.text}`);
// Process reply: auto-respond, forward to CRM, etc.
break;
}
// Always respond quickly — process asynchronously if needed
res.sendStatus(200);
});
app.listen(3000, () => console.log('Webhook handler listening on port 3000'));
Best practices
- Use HTTPS -- Your callback URL must use HTTPS for security.
- Respond quickly -- Return a
200 OKresponse within a few seconds. Process the payload asynchronously if needed. - Handle retries -- If your endpoint is unreachable, the system may retry delivery. Make your processing idempotent.
- Validate the source -- Verify that incoming webhooks originate from the Qlara Platform API.
- Monitor failures -- If your webhook endpoint is consistently failing, check your server logs and ensure the URL is accessible.
Choosing between polling and webhooks
| Aspect | Polling | Webhooks |
|---|---|---|
| Latency | Depends on poll frequency | Near real-time |
| Complexity | Simple GET requests | Requires a public HTTPS endpoint |
| Scalability | Increases API calls at scale | Push-based, no extra API calls |
| Best for | Low volume, ad-hoc checks | High volume, real-time dashboards |
See the Webhooks API Reference and Message Delivery Status API Reference for full endpoint documentation.