> ## 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.

# Webhooks

> Receive signed HTTPS notifications when redemptions, links, catalog entries, prices and invoices change.

Webhooks tell your systems when something happens in your partner account, so you do not have to poll the API. AllRide sends an HTTPS `POST` with a JSON body to each of your webhook endpoints that subscribes to the event's type.

## How webhooks work

1. Something happens in the platform: a voucher is redeemed, a Redemption Link runs out, a voucher set becomes available in your catalog, an invoice is issued.
2. AllRide renders the event for your partner (the consumer view of the resource) and **stores** it, with a stable `event.id`, for every webhook endpoint of yours that subscribes to its type. For an active endpoint the delivery appears in your delivery log at once as `PENDING` and the send is queued; for a paused endpoint it is stored as `FAILED` with `endpoint-disabled` and can be replayed after you resume the endpoint.
3. Your endpoint verifies the `x-signing-signature` header, answers `2xx` within 15 seconds and processes the event afterwards.
4. Any other outcome is a failed attempt; the stored event is sent again with backoff, up to 5 attempts in total. The delivery ends as `DELIVERED` (a `2xx`) or `FAILED` (the last attempt failed, or no request could be sent; `lastErrorCode` says why), see [Retries, logs and replay](https://docs-consumer.voucher.allride.io/webhooks/retries-and-logs.md#why-a-delivery-failed-lasterrorcode).

Every delivery and each of its attempts (status code, response excerpt, time) is kept in the delivery log for 30 days, so you can see what was sent and what your endpoint answered.

You can subscribe to the 25 consumer event types listed in the [event catalog](https://docs-consumer.voucher.allride.io/webhooks/events.md): redemptions, redemption entities, Redemption Links, catalog changes, Price Terms, billing documents and payments, plus the test event.

> [!NOTE]
>
> Deliveries are made only while your partner is `active` **and** its API access is `active`. Events that occur in other periods are not delivered and cannot be replayed. The endpoint shows the reason in `deliveriesBlockedReason`.

## Register an endpoint

Register endpoints in the [developer dashboard](https://consumer-developers.voucher.allride.io/go/webhooks) or through the Workspace API with [Create a webhook endpoint](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-create.md). Both need the permission `manage-webhooks`, which you can delegate to API keys. Dashboard users additionally need a recent sign-in.

```bash theme={null}
curl -X POST "$VOUCHER_API_BASE_URL/partner/v1/webhook-endpoints" \
  -H "Authorization: Bearer $VOUCHER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d "{
    \"url\": \"$WEBHOOK_URL\",
    \"description\": \"Redemption notifications\",
    \"eventTypes\": [\"redemption-created\", \"redemption-voided\"]
  }"
```

The `201` response contains the endpoint and its two signing secrets, `firstSecret` and `secondSecret`. **They are shown only in this response**: store both in your secret manager straight away. An idempotent replay of the request returns the endpoint with the secrets set to `null` (header `Idempotent-Replay-Redacted: true`); if you lost the original response, rotate both secrets.

Rules for an endpoint:

* `eventTypes` holds 1 to 30 unique types that your partner may subscribe to; any other type fails with `400` [`data-failed-validation`](https://docs-consumer.voucher.allride.io/errors/catalog#data-failed-validation) and the constraint [`webhook-event-type-not-allowed`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-event-type-not-allowed). [List webhook event types](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-event-types-list.md) returns the types you can use.
* Billing types (`invoice-*`, `payment-failed`, `payment-method-deactivated`) need `view-consumer-billing`, and `price-terms-*` types need `view-consumer-pricing`, held by the user or API key that creates or changes the endpoint. Without it, every write on such an endpoint answers `403` [`missing-permission`](https://docs-consumer.voucher.allride.io/errors/catalog#missing-permission).
* A partner has at most 10 endpoints ([`webhook-endpoint-limit-reached`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-endpoint-limit-reached)).
* [Update](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-update.md) replaces `eventTypes` as a whole. [Pause](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-pause.md) and [resume](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-resume.md) stop and restart deliveries; events that occur while an endpoint is paused are logged as not delivered and can be replayed within 30 days of the event (resume the endpoint first). [Delete](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-delete.md) is final.
* Owners and your technical contact receive an email when an endpoint is created or its URL or event types change.

## Endpoint requirements

* **HTTPS only**, on port 443 or 8443, with a certificate that validates. Plain HTTP is refused.
* **Publicly resolvable.** URLs that resolve to private, loopback, link-local or cloud metadata addresses are refused when you save them (`422` [`webhook-url-not-allowed`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-url-not-allowed)) and again at send time.
* **Answer `2xx` within 15 seconds.** Acknowledge first, then do the work asynchronously (for example through your own queue). Any other status, a timeout or a connection error counts as a failure.
* **No redirects.** AllRide does not follow `3xx` responses; a redirect counts as a failure.
* **Allow AllRide's egress addresses** if your firewall filters inbound traffic. They are published in [Network and IP addresses](https://docs-consumer.voucher.allride.io/guides/network.md).
* **Verify every request** with the `x-signing-signature` header before you trust the body ([Verifying signatures](https://docs-consumer.voucher.allride.io/webhooks/verify-signatures.md)).

Each request carries these headers:

| Header | Value |
| - | - |
| `content-type` | `application/json` |
| `user-agent` | `AllRide-Voucher-Webhooks/1.0 (+https://docs-provider.voucher.allride.io/webhooks)` |
| `x-signing-signature` | `t=<unix seconds>,s1=<signature with your first secret>,s2=<signature with your second secret>` |

## Payload format

Every body is a JSON object of at most 64 KiB with two properties, `event` and `payload` (the format of AllRide's company webhooks):

| Property | Meaning |
| - | - |
| `event.id` | The event id, a UUID. It is the same on every attempt and replay of the event: deduplicate on it. |
| `event.type` | The event type in lower-case kebab case, for example `redemption-created`. |
| `event.timestamp` | When AllRide created the event, in Unix seconds (UTC). It does not change between attempts; the signing time is `t` in `x-signing-signature`. |
| `payload` | The resource the event is about. It starts with `resource` and `id`, followed by the resource's fields as the Consumer API represents it for you (with `_links`), or the fields the event type defines. |

```json theme={null}
{
  "event": {
    "id": "3f1c9a52-7b4e-4d21-9c6a-0e5d8b2f4a17",
    "type": "redemption-voided",
    "timestamp": 1793692800
  },
  "payload": {
    "resource": "redemption",
    "id": 6017,
    "status": "voided",
    "voucherSetId": 2030,
    "redeemedAt": "2026-10-21T17:42:10.120Z",
    "voidedAt": "2026-11-03T08:00:00Z",
    "voidReason": "Customer cancelled the order",
    "_links": {
      "self": { "href": "https://api.voucher.allride.io/consumer/v1/redemptions/6017", "type": "application/json" }
    }
  }
}
```

The example is shortened; each type's page in the [event catalog](https://docs-consumer.voucher.allride.io/webhooks/events.md) shows its full payload. `payload` is a snapshot taken when the event was created. To act on the latest state, fetch `_links.self`. Ignore properties and event types you do not know: new ones can be added without a new API version. A partner that is both a provider and a consumer receives the consumer view of its own redemptions here; the provider site documents the provider view.

> [!WARNING]
>
> Consumer `redemption-created` and `redemption-voided` payloads contain the issued voucher code or link and your `metadata`. Treat them like the API responses they mirror: store them securely and show issued values only to the entitled customer.

## Delivery guarantees

* **At least once.** An event can arrive more than once, for example after a timeout or a replay. Make handlers idempotent: deduplicate on `event.id`.
* **Unordered, possibly late.** Events can arrive in any order: retries arrive later than newer events, and events are stored before they are sent, so they are never silently dropped: if a send is delayed, an automatic sweep re-queues a delivery never attempted after about 10 minutes, or last attempted more than 30 minutes ago, so it may arrive late. A delivery that still cannot be sent after 3 re-queues becomes `FAILED` with `delivery-stalled` and can be replayed. Use `event.timestamp` (or the resource's own timestamps) to drop stale updates, and re-fetch the resource when the order matters.
* **Stored, retried, replayable.** Each event is stored before it is sent. A failed attempt is sent again with backoff (about 30 seconds growing to 10 minutes), up to 5 attempts in total; then the delivery is `FAILED` and no longer retried, but you can [replay it](https://docs-consumer.voucher.allride.io/webhooks/retries-and-logs.md#replay) while it is in the delivery log, 30 days after the event. A replay re-sends the same stored event with the same `event.id` and five new attempts; the log keeps counting (`attemptNo`, `attemptCount`, `replayCount`).
* **Signed at each send.** The body is stored at event time and signed when it is sent, so every attempt and replay carries a fresh `t` and fresh signatures while the body (including `event.id` and `event.timestamp`) stays byte for byte the same.

## Test events

[Send a test event](https://docs-consumer.voucher.allride.io/api-reference/partner-webhook-endpoints-send-test-event.md) delivers a [`webhook-endpoint-test`](https://docs-consumer.voucher.allride.io/webhooks/events/webhook-endpoint-test) event to one endpoint, even if the endpoint does not subscribe to that type. Use it to check reachability and your signature verification.

```bash theme={null}
curl -X POST "$VOUCHER_API_BASE_URL/partner/v1/webhook-endpoints/11069:send-test-event" \
  -H "Authorization: Bearer $VOUCHER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "message": "Hello from the integration test" }'
```

The API answers `202` with the `webhookDeliveryId` of the stored test event, which is `PENDING` in the log until it is `DELIVERED` or `FAILED`. The body your endpoint receives looks like this:

```json theme={null}
{
  "event": { "id": "0c5b7e2d-91a4-4f38-b6d2-7e3a1f9c0b54", "type": "webhook-endpoint-test", "timestamp": 1793610900 },
  "payload": { "resource": "webhook-endpoint", "id": 11069, "message": "Hello from the integration test" }
}
```

Test events need an `active` endpoint (a paused or deleted one answers `409` [`webhook-endpoint-not-active`](https://docs-consumer.voucher.allride.io/errors/catalog#webhook-endpoint-not-active)) and active API access. They are retried like any other event. The sandbox is a separate environment: register a sandbox endpoint there with sandbox API keys.

## Related

* [Verifying signatures](https://docs-consumer.voucher.allride.io/webhooks/verify-signatures.md)
* [Retries, logs and replay](https://docs-consumer.voucher.allride.io/webhooks/retries-and-logs.md)
* [Event catalog](https://docs-consumer.voucher.allride.io/webhooks/events.md)
* [Network and IP addresses](https://docs-consumer.voucher.allride.io/guides/network.md)
* [Idempotency](https://docs-consumer.voucher.allride.io/guides/idempotency.md)


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