Skip to main content
Receipts for your messages can wait for you in one durable queue per HTTP login until you acknowledge them. You read that queue over gRPC in either of two ways — a server stream (Subscribe) or polling (Pull) — and remove each receipt with Ack. Both read the same queue. It is the alternative to running a webhook endpoint: nothing to expose to the internet, nothing for the gateway to retry against, and receipts kept until you have them.

Which receipts land in the feed

A receipt is queued for your login when all of these hold:
  • the message was sent by your HTTP login — over gRPC or over REST;
  • it asked for receipts — dlr: true on gRPC, "dlr": "yes" on REST — and this receipt is one it asked for (dlr_level: 1 progress, 2 the outcome, 3 both);
  • it carries no dlr_url.
The webhook wins. A message sent with a dlr_url has its receipts sent to that URL and never to the feed. Messages sent over SMPP never appear in it — they get their receipts on the bind.
REST sends can use the feed too. On a platform with gRPC and the feed switched on, REST accepts "dlr": "yes" with no dlr_url, and those receipts land here. Where gRPC is off, that REST request is still refused 400, because the receipts would have nowhere to go. See POST /secure/send.
A message that reaches the receipt deadline — 24 hours by default — with no outcome gets one final UNKNOWN receipt in the feed, as a webhook would.

The Receipt message

The webhook’s JSON body, one field per key, plus two delivery fields at the end: Optional fields are absent, not empty, when there is nothing to say. Everything the delivery receipts guide says about level, stat and err applies unchanged.

Delivery is at least once

1

A receipt leaves the queue only when you Ack it

Or when retention removes it — see Limits and retention.
2

A delivered receipt is leased to you for 120 seconds

Not acknowledged by then, it is delivered again — to whichever Subscribe or Pull of your login asks next — with delivery_count up by one.
3

A stream that ends gives its receipts back at once

When a Subscribe stream ends — you cancel, the connection drops, the gateway restarts — the receipts it had delivered and you had not acknowledged are released immediately, not after the lease.
So after a reconnect you are sent again whatever you had not acknowledged. That is the replay, and it is what makes the feed safe across restarts on either side.
Make your handling idempotent, and acknowledge after, never before.
  • Key it on id plus level — a progress receipt and an outcome for one message are two receipts.
  • Treat a delivery_count above 1 as a possible repeat.
  • Ack only after you have stored or acted on a receipt. Acknowledging first and then crashing loses it for good.

Ack

already_gone is not an error. An Ack repeated after a timeout is safe, which is exactly when you will repeat one. More than 1000 tokens in one call is INVALID_ARGUMENT. A token works on any gateway node and across restarts, but only for the login it was delivered to. Batch your acknowledgements: one Ack of 500 tokens is far cheaper than 500 of one.

Pull: polling

Returns up to max receipts (at most 1000), oldest first.
  • With none waiting and wait_seconds above 0, it waits up to that long — 20 seconds at most — and returns as soon as one arrives. That is a long poll, and it is how you avoid a busy loop.
  • With wait_seconds: 0 it returns at once, possibly empty.
A typical loop:
Pull suits a batch job, a worker that wakes on a timer, or a language whose streaming support you would rather not depend on.

Subscribe: streaming

Streams your queue: what is already waiting first, then new receipts as they arrive. window bounds how many delivered-but-unacknowledged receipts the stream holds. At the window the stream pauses until you Ack — on any channel. Unset, or above the gateway’s maximum (1000 by default), the maximum is used.
A stream that seems to stop after exactly window receipts is waiting for your acknowledgements. Ack as you go, or in small batches — not at the end.

Several readers share the queue

Several Subscribe streams — and Pulls — of one login share the queue: each receipt goes to exactly one of them at a time. Use that to spread receipt handling over several workers. To give separate applications separate feeds, give them separate HTTP logins.

Ordering

Best effort, by arrival. There is no ordering guarantee across gateway nodes, and a redelivered receipt arrives after newer ones. An outcome can arrive before a progress receipt for the same message. Order by level — 2 is final — and by done_date, never by arrival. Once you hold a level: 2 receipt for a message, ignore any level: 1 that comes after it.

Limits and retention

Your provider can change each of these.
A login that asks for receipts and never reads its feed fills it. It reaches the cap, and from then on new receipts are not queued — the old, unread ones are kept instead. If you switch receipts on without a dlr_url, run something that reads them. If you never want them, send dlr: false.

Examples

Subscribe, Pull and Ack loops in Python, Java and Go.

The delivery receipt webhook

The same fields, POSTed to your URL instead.