> ## Documentation Index
> Fetch the complete documentation index at: https://developers.phonovation.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Delivery Receipts (DLRs)

> Receive real-time message delivery notifications via HTTP POST to your endpoint.

<Info>
  DLRs apply to **all** message sending methods — the Phonovation SMS API v2, the legacy v1 APIs, and messages sent directly through the Phonovation web interface. If a message is sent, a DLR can be generated.
</Info>

When a message is delivered (or fails), the Phonovation gateway sends an HTTP POST to a URL you provide. This lets you track delivery status in real time for every message you send.

## Setup

Provide your DLR callback URL to [support@phonovation.com](mailto:support@phonovation.com). The endpoint must:

* Accept `HTTP POST` requests
* Return a `200 OK` response

## DLR parameters

The POST body contains the following fields:

| Parameter         | Example                            | Description                                         |
| ----------------- | ---------------------------------- | --------------------------------------------------- |
| `SMS-Type`        | `Notification`                     | Always `Notification`                               |
| `SMS-NotifyId`    | `321354`                           | The `notifyId` you assigned to the recipient        |
| `SMS-Success`     | `True`                             | `True` if delivered, `False` if not                 |
| `SMS-To`          | `353871111111`                     | Destination number (GSM format)                     |
| `SMS-From`        | `353877777777`                     | Sender ID as it appeared on the recipient's handset |
| `SMS-Verify`      | `9db038748b3088971d913616c13e769b` | MD5 hash for payload verification — see below       |
| `SMS-TotalToSend` | `1`                                | Total number of messages in the send                |
| `SMS-TotalSent`   | `1`                                | Number successfully sent                            |
| `SMS-TotalFailed` | `0`                                | Number that failed                                  |
| `SMS-TimeStamp`   | `2024-01-10 14:17:44`              | Delivery time (`yyyy-MM-dd HH:mm:ss`, UTC)          |

## Success logic

`SMS-Success` is `True` only when all messages were delivered:

```text title="DLR success rule" icon="circle-check" wrap theme={"dark"}
TotalSent == TotalToSend AND TotalFailed == 0  →  True
Otherwise  →  False
```

| TotalToSend | TotalSent | TotalFailed | SMS-Success |
| ----------- | --------- | ----------- | ----------- |
| 2           | 2         | 0           | `True`      |
| 2           | 1         | 1           | `False`     |

## DLR timing

The vast majority of DLRs — **90%+** — arrive within seconds to a few minutes of sending. However, DLRs are ultimately dependent on the downstream mobile network and the recipient's device.

In some cases a DLR may be delayed by up to **48 hours** due to:

* Network congestion or outages
* High traffic volumes
* The recipient's device being powered off or out of coverage

**Design your integration to handle late DLRs.** Do not assume a message has failed simply because a DLR has not arrived quickly. If you require a hard timeout, 48 hours is a safe threshold after which you can treat a missing DLR as unconfirmed.

## Example DLR payload

```text title="Example DLR payload" icon="receipt" highlight={2,3,6} wrap theme={"dark"}
SMS-Type=Notification
SMS-NotifyId=
SMS-Success=True
SMS-To=353861234567
SMS-From=353871234568
SMS-Verify=5fb4926bd7e461eddc6dc629f472bab4
SMS-TotalToSend=1
SMS-TotalSent=1
SMS-TotalFailed=0
SMS-TimeStamp=2026-04-09 16:03:16
```

<Warning>
  URL-encode the POST body before sending. Some field values may contain characters that break unencoded requests.
</Warning>

## Verifying the payload

The `SMS-Verify` field is an MD5 hash you can use to confirm the DLR genuinely came from Phonovation:

```mermaid title="DLR verification flow" theme={"dark"}
flowchart TD
  Receive["Receive DLR POST"] --> Extract["Read SMS-NotifyId, SMS-Success, and SMS-Verify"]
  Extract --> Hash["Compute MD5(SMS-NotifyId + SMS-Success + Client Secret)"]
  Hash --> Compare{"Hash matches SMS-Verify?"}
  Compare -->|Yes| Accept["Accept payload"]
  Compare -->|No| Reject["Reject or investigate payload"]
```

```text title="Verification formula" icon="lock" wrap theme={"dark"}
MD5(SMS-NotifyId + SMS-Success + YourClientSecret)
```

<CodeGroup>
  ```bash title="cURL" icon="terminal" wrap theme={"dark"}
  curl -X POST https://example.com/sms/dlr \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "SMS-NotifyId=order-001&SMS-Success=True&SMS-Verify=EXPECTED_MD5_HASH"
  ```

  ```js title="JavaScript" icon="js" lines focus={4-8} theme={"dark"}
  const crypto = require('crypto');

  function verifyDlr({ notifyId, success, clientSecret, verify }) {
    const expected = crypto
      .createHash('md5')
      .update(notifyId + success + clientSecret)
      .digest('hex');
    return expected === verify;
  }
  ```

  ```python title="Python" icon="python" lines focus={4-6} theme={"dark"}
  import hashlib

  def verify_dlr(notify_id, success, client_secret, verify):
      expected = hashlib.md5(
          f"{notify_id}{success}{client_secret}".encode()
      ).hexdigest()
      return expected == verify
  ```

  ```csharp title="C#" icon="code" lines focus={4-9} theme={"dark"}
  using System.Security.Cryptography;
  using System.Text;

  bool VerifyDlr(string notifyId, string success, string clientSecret, string verify)
  {
      var input = Encoding.UTF8.GetBytes(notifyId + success + clientSecret);
      var hash = MD5.HashData(input);
      var expected = Convert.ToHexString(hash).ToLowerInvariant();
      return expected == verify;
  }
  ```

  ```java title="Java" icon="java" lines focus={5-11} theme={"dark"}
  import java.security.MessageDigest;
  import java.util.HexFormat;

  boolean verifyDlr(String notifyId, String success, String clientSecret, String verify) throws Exception {
      MessageDigest md5 = MessageDigest.getInstance("MD5");
      byte[] hash = md5.digest((notifyId + success + clientSecret).getBytes());
      String expected = HexFormat.of().formatHex(hash);
      return expected.equals(verify);
  }
  ```

  ```php title="PHP" icon="php" lines focus={2-3} theme={"dark"}
  function verifyDlr($notifyId, $success, $clientSecret, $verify) {
      $expected = md5($notifyId . $success . $clientSecret);
      return $expected === $verify;
  }
  ```
</CodeGroup>

## Tracking recipients with notifyId

Set a `notifyId` per recipient when sending and Phonovation will echo it back in every DLR for that recipient via `SMS-NotifyId`. The same identifier also appears in webhooks, making it the single thread that ties your outbound send, webhook confirmation, DLR, and inbound replies together.

See [notifyId](/guides/notify-id) for full details, use cases, and end-to-end flow examples.
