Delivery receipts
A receipt arrives as deliver_sm on a receiver or transceiver session of your account. One
comes for every submit_sm — for every part of a long message — whatever you put in
registered_delivery.
What the receipt looks like
The text of the receipt is in the standard form:
id:4815162342 sub:001 dlvrd:001 submit date:2610301432 done date:2610301433 stat:DELIVRD err:000 text:
| Field | What it means |
|---|---|
id: | The message_id from submit_sm_resp |
sub: | Always 001 |
dlvrd: | 001 if the message is delivered, 000 otherwise |
submit date: | When the message was handed to the operator (for REJECTD — when it was rejected), YYMMDDhhmm |
done date: | When we recorded the outcome, YYMMDDhhmm |
stat: | The outcome, see below |
err: | The error code, see below |
text: | Always empty |
Both dates are in Kyiv time (Europe/Kyiv) and go to the minute.
The same message_id is also in the receipted_message_id field (TLV 0x001E), and the outcome
as a number in message_state (TLV 0x0427). Other fields of the PDU:
| Field | Value |
|---|---|
source_addr | The sender name the message went out under |
destination_addr | The recipient's number |
esm_class | 0x04 — a delivery receipt |
data_coding | 0, or 8 if the text cannot be written in GSM 7-bit |
The addresses are not swapped: the sender stays in source_addr, the recipient in
destination_addr. Many SMSCs put the recipient first, so match receipts to messages by id:
or receipted_message_id, not by addresses.
The outcome: stat:
stat: | message_state | What happened |
|---|---|---|
DELIVRD | 2 | The operator confirmed delivery to the phone |
ENROUTE | 1 | On its way: the final receipt with the same id: follows |
ACCEPTD | 6 | Accepted by the operator |
EXPIRED | 3 | The validity period ran out before the phone came back online, or before the message could be sent |
UNDELIV | 5 | The operator did not deliver it |
REJECTD | 8 | The message was not accepted for sending — the reason is in err: |
DELETED | 4 | The message was cancelled |
UNKNOWN | 7 | The operator reported no outcome |
If the operator never reports an outcome, no receipt comes. A message that runs out of
validity while still waiting on our side gets EXPIRED, and the money for it is returned.
Why a message was rejected: err:
For REJECTD the code says what to fix:
err: | Reason | What to do |
|---|---|---|
001 | Not enough money on the balance | Top up and send again |
002 | The recipient: the number is invalid, blocked, or its operator is not served | Check the number |
003 | The sender name is not allowed for this account | Use an approved name, or ask support |
004 | The text: empty, too long, or a flash message longer than one part | Shorten or fix the text |
005 | The route does not carry this kind of message | Ask support |
006 | Sending is temporarily switched off on our side | Send again later |
000 | Another reason | Ask support, with the id: |
On receipts that come from the operator (DELIVRD, UNDELIV and the rest), err: is the
operator's own code, passed on as is.
Acknowledging receipts
Answer every receipt with deliver_sm_resp. Up to twenty receipts are in flight at once, and
the next ones follow as soon as you acknowledge the ones already sent.
Any answer removes the receipt for good — a non-zero command_status included. If your
side could not process a receipt, do not expect it again after answering with an error: keep
it and process it later instead.
A receipt you have not acknowledged is kept for ten days and comes over your next session.
Several sessions
A receipt goes to one of the receiver and transceiver sessions of the account, not necessarily
the one the message was sent through. Now and then the same receipt can reach two sessions —
de-duplicate by id:.
Receipts exist only for messages sent over SMPP. For messages sent through the HTTP API the outcome comes to your notification URL. Incoming messages are not delivered over SMPP.