iletiMerkezi Webhooks
Webhooks deliver real-time status updates for the messages you sent via send-sms. You configure a URL in the panel; iletiMerkezi POSTs to that URL on every status change (acceptance, delivery, failure). Webhooks are more efficient and lower-latency than polling get-report.
Setup
- Open
panel.iletimerkezi.com→ Settings → API → Notification URL (Turkish: "Bildirim Adresi"). - Enter your webhook URL (e.g.,
https://yourdomain.com/api/iletimerkezi/webhook) and click Add to save. - The URL must be HTTPS and reachable from the public internet. iletiMerkezi servers will
POSTto it.
Only one webhook URL per account is supported. If you need to dispatch the same report to multiple destinations, fan out from your webhook handler.
Once the URL is set, every
send-smsorder's delivery reports flow into it automatically — no extra API call needed.
Request
For each message status update, iletiMerkezi POSTs to your URL with:
POST https://yourdomain.com/api/iletimerkezi/webhook
Content-Type: application/json{
"report": {
"id": 1599558518,
"packet_id": 104525848,
"status": "accepted",
"to": "+905XXXXXXXXX",
"body": "Message text"
}
}Fields
report.id(integer): unique ID of a single message inside an order.report.packet_id(integer): the order ID this message belongs to. Matchesorder.idreturned bysend-sms; all webhook events from one order share thispacket_id.report.status(string): message status (see table below).report.to(string): recipient number with+90prefix.report.body(string): the message text that was sent.
Status values (report.status)
| Value | Meaning | |---|---| | accepted | Message was accepted and queued for delivery | | delivered | Message was successfully delivered to the recipient | | undelivered | Message could not be delivered (off network, blacklist, format, etc.) |
get-reportreturns numeric codes (110,111,112); webhooks send string values (accepted,delivered,undelivered). For one message you typically receiveacceptedfirst, then eitherdeliveredorundelivered.
Response
Your endpoint should reply with 2xx (e.g., 200 OK). A non-2xx response can cause iletiMerkezi to flag the webhook as failed; respond with 200 quickly and process work asynchronously.
HTTP/1.1 200 OK
Content-Type: application/json
{"received": true}Security
iletiMerkezi webhooks currently do not send an HMAC signature or X-Signature-style header; the payload is the raw report object. You must enforce at least one verification layer on the receiver side in production.
Recommended pattern: secret token in the URL. Configure the webhook URL in the panel with a token query parameter; your handler compares that token against a stored value and rejects mismatches with 401. This is the same pattern our internal services use to consume iletiMerkezi DLRs.
POST https://yourdomain.com/api/iletimerkezi/webhook?token=SECRET_VALUE// Handler example (Node/Express)
app.post('/api/iletimerkezi/webhook', (req, res) => {
if (req.query.token !== process.env.WEBHOOK_TOKEN) {
return res.status(401).end();
}
// ... process payload
res.status(200).json({ received: true });
});Extra defenses:
- Treat the webhook URL like a secret — keep it in env vars, do not commit it.
- HTTPS is enforced by the panel; HTTP URLs are rejected.
- Match incoming
report.packet_idagainst an order ID you wrote to your own DB. Silently drop reports you can't match (see "Multi-app filtering" below).
Native HMAC signing is a roadmap candidate; it does not exist today. The URL-token convention is the real protection layer right now.
Common pitfalls
All SMS on the account land on a single webhook URL (multi-app filtering). If the same iletiMerkezi account is shared by multiple apps, you will receive DLRs that do not belong to your app. On the receiver side, look for an app-specific marker inside
report.body(e.g., your short-link domainiim.to/..., a campaign tag, an order-ID prefix) and silently200 OKanything that doesn't match. Otherwise you will write orphan DLRs into your DB and treat them as your own.// Practical example: short-link domain as the marker if (!report.body.includes('iim.to')) { return new Response('OK', { status: 200 }); // not ours, drop silently }The same
report.idcan arrive more than once; write idempotently. Carrier or iletiMerkezi-side retries can repeat a status update. Practical pattern: on the first callback, writedelivery_received_at+ status; on subsequent callbacks, only refresh the status (don't touch the timestamp). Idempotency key:report.id + report.status.packet_idequals thesend-smsorder.id. Use it to correlate webhooks with the originating order; multiple message webhooks from the same order share thispacket_id.acceptedis not "delivered". It means "accepted, queued". Wait fordeliveredfor actual delivery; anundeliveredwebhook is the failure terminus.Respond fast. Push DB writes and downstream API calls onto a background queue, then return
200from the webhook handler immediately. A slow handler triggers retries.HTTPS only. HTTP URLs are not accepted by the panel. Use ngrok or a cloudflared tunnel for local development.
No webhook = polling. If the webhook URL is empty, you can only learn the delivery state via
get-report. Webhooks are recommended in production; you can keep polling as a fallback.
Related
Last updated: 2026-04-29 · Türkçe