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

# Retries, logs and replay

> How failed webhook deliveries are retried, how to read the delivery log, and how to replay events.

A **delivery attempt** is one HTTP request to your endpoint. An attempt succeeds when your endpoint answers with any `2xx` status within 15 seconds. Everything else is a failure: a `3xx` (redirects are not followed), `4xx` or `5xx` status, a timeout, a TLS, DNS or connection error, or a URL that resolves to an address AllRide refuses to call.

## Retry schedule

When an event occurs, AllRide first **stores** it, with its complete body, for every endpoint that subscribes to its type, in the same transaction as the change that caused it. The delivery shows up in the log at once as `PENDING`. Only then is the send triggered through AllRide's delivery queue (Google Cloud Pub/Sub). The sender acknowledges the queue message only when your endpoint answered `2xx` (the delivery becomes `DELIVERED`); after a failed attempt the message is not acknowledged and the queue delivers it again:

* with an increasing backoff, starting at about **30 seconds** and growing to at most **10 minutes** between attempts;
* at most **5 delivery attempts** in total. When the fifth attempt fails the delivery becomes `FAILED` and is no longer retried; you can still [replay](#replay) it while it is in the delivery log, 30 days after the event;
* with the body stored at event time, byte for byte (same `event.id` and `event.timestamp`), signed again at each send: `t` in `x-signing-signature` and the signatures differ between attempts while the body stays identical;
* `Retry-After` headers in your responses are not honoured; the backoff is fixed by the platform.

**Stored first, so never silently dropped.** If triggering a send fails or is lost, an automatic sweep re-queues the delivery: a delivery that was never attempted after about **10 minutes**, and a delivery whose last attempt was more than **30 minutes** ago and that is still `PENDING`. A delivery that still cannot be sent after **3 re-queues** becomes `FAILED` with `lastErrorCode` `delivery-stalled` (a fault on AllRide's side, which AllRide monitors), and you can [replay](#replay) it. Deliveries can therefore arrive late and out of order: do not rely on the order of deliveries: deduplicate on `event.id` and use `event.timestamp` or the resource's own timestamps to discard stale updates.

> [!TIP]
>
> Acknowledge with `204` as soon as the signature checks out and queue the work on your side. A receiver that does slow work before it answers turns a busy period into timeouts and retries.

## Automatic disabling

AllRide **never disables an endpoint automatically**, however many deliveries fail. Deliveries stop only when:

* you [pause](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-pause.md) or [delete](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-delete.md) the endpoint. While an endpoint is paused, its events are stored as `FAILED` deliveries with `lastErrorCode` `endpoint-disabled` and no attempt, and can be replayed within 30 days of the event ([resume](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-resume.md) the endpoint first);
* your partner is not `active`, or its API access is not `active`. Events in such a period are neither delivered nor logged and cannot be replayed. Each endpoint shows the reason in `deliveriesBlockedReason` (`api-access-inactive` or `partner-inactive`, else `null`).

Watch the delivery log (`filter[status]=FAILED`) or alert on your own receiver's error rate: a delivery that fails 5 times stays `FAILED` until you replay it.

## Delivery logs

The delivery log has one **delivery** per stored event and endpoint, kept for **30 days**. Read it in the [developer dashboard](https://consumer-developers.voucher.allride.io/go/webhook-deliveries) or with [List deliveries](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-deliveries-list.md) and [Get a delivery](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-deliveries-get.md) (permission `view-webhook-deliveries`, delegable to API keys).

A delivery is listed as soon as its event is stored, before the first send:

| `status` | Meaning |
| - | - |
| `PENDING` | Stored and queued, or waiting for the next retry. Also the state after you replay a delivery. |
| `DELIVERED` | Your endpoint answered `2xx`; `deliveredAt` says when. |
| `FAILED` | The last of the 5 attempts failed, or no request could be sent (see [`lastErrorCode`](#why-a-delivery-failed-lasterrorcode)). Replay it to send it again. |

| Field | Meaning |
| - | - |
| `webhookEventId`, `eventType` | `event.id` and `event.type` of the body. Identical on every attempt and replay. |
| `webhookEndpointId`, `url` | The endpoint and the URL of the latest attempt. |
| `status`, `lastStatusCode`, `lastErrorCode`, `deliveredAt` | Where the delivery stands. |
| `attemptCount`, `replayCount` | The total number of attempts (the length of `attempts[]`, across replays) and how often you replayed the delivery. |
| `payload` | The body as stored at event time, which every attempt sends unchanged, with sensitive values masked (see below). |
| `attempts[]` | One entry per HTTP attempt, oldest first: `attemptNo` (1, 2, …; never restarted by a replay), `trigger` (`event`, or `replay` for a send you requested), `signature` (the `x-signing-signature` value of that attempt; secrets are never logged), `statusCode` (`null` without an HTTP response), `responseBody` (the first 4 KiB of your response; your content, so display it as text, never as HTML), `errorCode` (see the [table below](#why-a-delivery-failed-lasterrorcode)), `durationMs` and `attemptedAt`. |
| `createdAt`, `updatedAt` | When the event was stored and when the delivery last changed. |

### Why a delivery failed (`lastErrorCode`)

`lastErrorCode` is the `errorCode` of the last attempt, or one of the delivery-level codes below when no request was sent for it. Every `FAILED` delivery can be [replayed](#replay) while it is in the log.

| `lastErrorCode` | Level | Meaning |
| - | - | - |
| `timeout` | attempt | No complete response within 15 seconds |
| `connection-refused` | attempt | The connection was refused or reset |
| `dns-error` | attempt | Your host name did not resolve |
| `tls-error` | attempt | The TLS handshake or certificate check failed |
| `ssrf-blocked` | attempt | Your host resolved to an address AllRide does not call (private or internal ranges) |
| `http-4xx` | attempt | Your endpoint answered `4xx` (including `410` and `429`) |
| `http-5xx` | attempt | Your endpoint answered `5xx` |
| `redirect-not-followed` | attempt | Your endpoint answered `3xx`; redirects are never followed |
| `internal-error` | attempt | A fault on AllRide's side while sending; the attempt is retried |
| `endpoint-disabled` | delivery | The endpoint was paused or deleted when the event was stored or sent |
| `partner-inactive` | delivery | Your partner or its API access was not `active` when the delivery was due |
| `endpoint-unsubscribed` | delivery | The endpoint no longer subscribes to the event type |
| `subject-erased` | delivery | The redemption entity the event describes was erased, so the payload is gone |
| `delivery-stalled` | delivery | The delivery could not be sent after 3 automatic re-queues (a fault on AllRide's side) |
| `attempt-lost` | delivery | The fifth attempt was started but its result was never recorded (AllRide's sender stopped during it); your endpoint may have received it |

Filter the list by `webhookEndpointId`, `status`, `eventType`, `lastStatusCode` (`gte`, `lte`), `webhookEventId` and `createdAt`; the default order is newest first.

```bash theme={null}
curl -G "$VOUCHER_API_BASE_URL/partner/v1/webhook-deliveries" \
  -H "Authorization: Bearer $VOUCHER_API_KEY" \
  --data-urlencode "filter[webhookEndpointId]=11069" \
  --data-urlencode "filter[status]=FAILED" \
  --data-urlencode "sort=-createdAt"
```

**Masking.** Your endpoint receives the real body. The log stores a masked copy, because the log permission is held by roles that may not see voucher values or customer data:

* `issued.code` and `issued.link` of `redemption-created` and `redemption-voided` are masked;
* a redemption entity's `email` becomes the first character of the local part, `••••` and the domain (`a••••@example.com`); its `refId` and `displayName` become `"••••"`.

**Billing and price events.** Deliveries of billing types are visible only to principals that also hold `view-consumer-billing`, and deliveries of `price-terms-*` types only to principals with `view-consumer-pricing`. For others they are left out of lists, and reading or replaying one answers `404` [`webhook-delivery-not-found`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-delivery-not-found).

**Erased entities.** After you [erase a redemption entity](https://docs-consumer.voucher.allride.io/consumer/redemption-entities.md), the events that concern it have no payload any more: their deliveries show `payload: null` and they cannot be replayed.

To keep the log as a file, page through [List webhook deliveries](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-deliveries-list.md) as described in [Downloading large lists](https://docs-consumer.voucher.allride.io/guides/pagination.md#downloading-large-lists); the rows hold the same masked bodies.

## Replay

Replays re-send an event that your endpoint missed, for example after an outage. They need `manage-webhooks` (delegable to API keys), an `active` endpoint (else `409` [`webhook-endpoint-not-active`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-endpoint-not-active)) and active API access (else `403` [`api-access-inactive`](https://docs-consumer.voucher.allride.io/errors/catalog#api-access-inactive)). A delivery can be replayed, singly or in bulk, while it is in the delivery log: **30 days** after its event was created. After that the delivery is gone (`404` [`webhook-delivery-not-found`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-delivery-not-found)). An event whose payload was removed because its entity was erased answers `410` [`webhook-event-expired`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-event-expired). A replay ignores the endpoint's current subscription: you asked for it explicitly.

**One delivery.** [Replay a delivery](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-deliveries-replay.md) re-sends the same stored event: the same body and `event.id`, with a fresh `t` and signatures. It answers `202` with the `webhookDeliveryId`; the delivery goes back to `PENDING`, its new attempts are added with `trigger: replay`, and it is retried like any other delivery. A replay starts **five new attempts**. The log keeps counting: `attemptNo` continues after the earlier attempts (a `FAILED` delivery with five attempts that is replayed and then answered `2xx` shows `attemptNo` 1 to 6), `attemptCount` is the total number of attempts (the length of `attempts[]`), and `replayCount` says how often the delivery was replayed.

```bash theme={null}
curl -X POST "$VOUCHER_API_BASE_URL/partner/v1/webhook-deliveries/21069:replay" \
  -H "Authorization: Bearer $VOUCHER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

**All failed events of a time window.** [Replay failed deliveries](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-replay-failed.md) re-sends, oldest first, every stored event of one endpoint in `[since, until)` whose delivery is `FAILED` (all attempts failed, or stored while the endpoint was paused), optionally only for some `eventTypes`. Each one goes back to `PENDING` with its original `event.id`.

```bash theme={null}
curl -X POST "$VOUCHER_API_BASE_URL/partner/v1/webhook-endpoints/11069:replay-failed" \
  -H "Authorization: Bearer $VOUCHER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "since": "2026-11-01T00:00:00Z", "until": "2026-11-02T00:00:00Z", "eventTypes": ["redemption-created"] }'
```

```json theme={null}
{
  "data": {
    "resource": "webhook-replay-result",
    "replayedCount": 37,
    "truncated": false,
    "nextSince": null,
    "_links": {
      "deliveries": {
        "href": "https://api.voucher.allride.io/partner/v1/webhook-deliveries?filter%5BwebhookEndpointId%5D=11069&filter%5Bstatus%5D=PENDING",
        "type": "application/json"
      }
    }
  }
}
```

* `since` may be at most 30 days in the past (else `400` [`data-failed-validation`](https://docs-consumer.voucher.allride.io/errors/catalog#data-failed-validation) with the constraint [`out-of-range`](https://docs-consumer.voucher.allride.io/errors/catalog#out-of-range)); `until` defaults to now.
* One call queues at most **10,000** events. When more match, `truncated` is `true`: call again with `since` set to `nextSince` once the first batch has gone out.
* The events are sent in the background; follow them in the log with `filter[status]=PENDING`. While a bulk replay of the same endpoint is still running, a second call answers `409` [`invalid-state-transition`](https://docs-consumer.voucher.allride.io/errors/catalog#invalid-state-transition). Pausing the endpoint stops the remaining sends.
* Billing and `price-terms-*` events are included only when you hold the matching read permission; events of erased entities are skipped and not counted.

Because a replay delivers an event again, your handler must be idempotent (see [Delivery guarantees](https://docs-consumer.voucher.allride.io/webhooks/overview.md#delivery-guarantees)).

## Related

* [Webhooks](https://docs-consumer.voucher.allride.io/webhooks/overview.md)
* [Verifying signatures](https://docs-consumer.voucher.allride.io/webhooks/verify-signatures.md)
* [Event catalog](https://docs-consumer.voucher.allride.io/webhooks/events.md)
* [Pagination, filtering and sorting](https://docs-consumer.voucher.allride.io/guides/pagination.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.