> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# SMPP: per-tenant no-delivery receipt window (receipt_timeout_seconds)

> How long a routed channel has to confirm delivery before your SMPP bind receives a UNDELIV receipt — set per deliver hop on a termination rule, 10–120 seconds, platform default 30.

# SMPP: the no-delivery receipt window

When a `submit_sm` from your SMPP bind is terminated on another channel — WhatsApp is the common case — no carrier produces a receipt, so Orbit synthesizes one. A receipt that says "nothing confirmed" is only useful if it arrives at a time you chose: this page covers the `receipt_timeout_seconds` field on a termination rule's `deliver` hop, which sets how long the channel has to confirm delivery before your bind receives a UNDELIV receipt you can fail over on.

The model behind the field is defined in [Send-side DLR model](/concepts/send-side-dlr-model); this page is the how-to: where to set it, when to change it, and the edge cases.

## What the window controls

An accepted absorbed submit arms a deadline at submit time plus the window. If a delivery or read receipt lands before the deadline, it is dispatched to your bind normally and the deadline is consumed. If the deadline expires with no confirmation, the sweep emits a `stat:UNDELIV` receipt whose reason names the armed window — `no delivery within 45 s of acceptance` — so your fallback logic both fires and can see which window fired it.

The field on the hop:

| Property    | Value                                                                                                       |
| ----------- | ----------------------------------------------------------------------------------------------------------- |
| Field       | `receipt_timeout_seconds` on the `deliver` hop of a termination rule                                        |
| Valid range | integer, 10–120 seconds inclusive (anything outside is rejected at rule-write time with a validation error) |
| Default     | 30 seconds when the field is omitted                                                                        |
| Scope       | only the UNDELIV-on-timeout arm of absorbed SMPP traffic that requests a receipt (`registered_delivery`)    |

The default of 30 seconds means an untouched rule behaves exactly as it did before the field existed: nothing about your existing receipts changes until you set a value.

In the dashboard, **Developer → SMPP** surfaces a **No-delivery receipt window** panel that previews the deliver-hop field with the same 10–120 bounds and blank-means-default behavior. The panel is a preview today — pick the value there and Support applies it to your rule while the unified rules editor is finalized. The API body below is the same contract you can write immediately.

## When to change it

Tune the window against your own traffic's confirmation curve, not against the platform default:

* **Raise it (toward 120)** when destinations confirm slowly — handsets that wake late, recipients roaming across MNOs, or channels with known receipt lag. A UNDELIV fired at 30 s on a channel that habitually confirms at 40 s trains your fallback to re-send messages that were about to land.
* **Lower it (toward 10)** when your fallback is the point — OTP and fraud-alert traffic where a fast re-send over a second channel beats the small hit-rate gain from waiting longer.
* **Leave it at 30** unless you have measured otherwise. Two tenants disagree on the right value by tens of seconds; the default exists as a safe middle, not a recommendation.

Measure before you tune: the message row's status timeline shows how long confirmations actually take on each channel, which is the curve you are setting the window against.

## A worked example — absorb onto WhatsApp with a 45-second window

The rule below absorbs SMPP `submit_sm` traffic for `+91` destinations onto WhatsApp and sets a 45-second window on the deliver hop:

```json theme={null}
{
  "name": "OTP via WhatsApp — 45s fallback window",
  "priority": 10,
  "enabled": true,
  "mode": "enforce",
  "direction": "mt_absorb",
  "ingress_kind": "smpp_submit",
  "match": {
    "all": [
      { "field": "dest_addr", "op": "prefix", "value": "+91" }
    ]
  },
  "actions": [
    {
      "kind": "deliver",
      "channel": "whatsapp",
      "template_name": "verify_user",
      "template_language": "en",
      "on_failure": "next",
      "receipt_timeout_seconds": 45
    },
    { "kind": "absorb" }
  ]
}
```

Write it against the rules endpoint:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messaging/termination/rules \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d @rule.json
```

With the rule enforced:

1. At **t=0** your `submit_sm` (with `registered_delivery` requesting a receipt) matches the rule and is absorbed onto WhatsApp. A deadline is armed at t=45 s.
2. If WhatsApp confirms delivery at **t=12**, a `stat:DELIVRD` receipt is dispatched to your bind at t=12 — the deadline never fires.
3. If nothing confirms by **t=45**, the sweep emits `stat:UNDELIV` with the reason `no delivery within 45 s of acceptance`, and your failover channel kicks in on exactly that signal.

In the dashboard, the **No-delivery receipt window** panel on Developer → SMPP shows `45` in the field once Support has applied it — the same value the API body carries.

## How it interacts with the canonical DLR vocabulary

The window touches exactly one arm of the receipt model: the synthesized `failed{provider_error}` → `UNDELIV` that fires on deadline expiry. Timing of the timeout arm is all it delays. Everything else is untouched:

* Receipts that arrive before the window dispatch normally — this is a deadline, not a grace period that holds receipts back.
* The failure arm still carries the documented `err:` tokens — `81` (not on destination channel), `88` (re-engagement required), `89` (rate limit), `00` (unspecified provider error) — per [Send-side DLR model](/concepts/send-side-dlr-model).
* The encodings the bind and the webhook leg produce are identical, per [SMPP edge model](/concepts/smpp-edge-model).

## API-side notes

* **Optional, default 30.** Omitting `receipt_timeout_seconds` arms the platform default. Sending the field as blank/absent and sending it as `30` are equivalent; there is no way to "unset" a rule into a non-default.
* **Integers only.** `25.5` fails validation at rule-write time; whole seconds in the 10–120 range only.
* **Shadow vs. enforce.** A shadow-mode rule evaluates and records without moving traffic; the window you set takes effect only once the rule flips to `enforce`. Rules without a `deliver` hop (pure `absorb` chains) have nothing to arm and ignore the field.
* **Receipt requests still govern.** The deadline only fires for submissions whose `registered_delivery` flag asked for a receipt; a submission that requested none is not given a timeout receipt either.
* **Rule-level guard on bind mode.** A rule that moves messages off the SMPP ingress can require the credential's `dlrMode` to be `webhook` or `both` (`require_dlr_mode` on the rule), so choosing a window never creates a receipt black hole for bind-only credentials.

## See also

* [Connect via SMPP](/guides/smpp) — bind credentials, DLR delivery modes, and the diagnostic ladder
* [Send-side DLR model](/concepts/send-side-dlr-model) — the canonical vocabulary and the absorbed-receipt encoder this window tunes
* [SMPP edge model](/concepts/smpp-edge-model) — the relay and reconciliation model underneath the bind
* [DLR model: two planes, one vocabulary](/concepts/dlr-model-two-planes) — telling carrier-sourced receipts from synthesized ones
