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

# Requests and responses

> Formats, envelopes, ids, time, money, updates and concurrent changes.

## Format

* Requests and responses are JSON in UTF-8. Send `Content-Type: application/json` on requests with a body; a body that is not valid JSON is 400 `malformed-json`.
* Property and query parameter names are `camelCase`; enum values, error codes and event types are `kebab-case`.
* Unknown request properties and query parameters are refused (400 `data-failed-validation` with field code `unknown-field` or `unknown-parameter`), so typos never get ignored silently.
* `Accept-Language: de` or `en` selects the language of money labels and of emails a request triggers (error `message`s are English); the response carries `Content-Language`. Localised content you stored is always returned in full.
* Every response carries `X-Request-Id` (yours if you sent a valid one, else generated) and `Cache-Control: no-store`. Quote the request id when contacting support.

## Response envelopes

| Case | Status | Body |
| - | - | - |
| One resource, command result | 200 / 201 | `{"data": {…}}` |
| List | 200 | `{"data": {"data": [ … ], "page": 1, "perPage": 25, "totalPages": 5, "total": 112}}` |
| Asynchronous job accepted | 202 | `{"data": {…job…}}` with `Location` and `Retry-After` |
| Delete | 204 | no body |
| Error | 4xx / 5xx | `{"errors": [{"message", "statusCode", "errorCode", …}]}` (see [Errors](https://docs-provider.voucher.allride.io/errors.md)) |

```json theme={null}
{
  "data": {
    "id": 2030,
    "resource": "voucher-set",
    "internalName": "Ride credit EUR 5 - Q4",
    "status": "scheduled",
    "createdAt": "2026-10-20T09:12:44.501Z",
    "_links": {
      "self": { "href": "https://api.voucher-staging.allride.io/provider/v1/voucher-sets/2030", "type": "application/json" }
    }
  }
}
```

Every resource has `id`, `resource` (a constant kind such as `voucher-set`), domain fields, timestamps and `_links` to related resources. Nullable fields are always present, with `null` when unset; empty lists are `[]`.

## Identifiers

Ids are positive integers (`"id": 42`), in paths and bodies. A malformed id (`/voucher-sets/abc`) is 400 `data-failed-validation` with field code `invalid-id`. A well-formed id that does not exist **or belongs to another partner** is 404 with the resource's code, for example `voucher-set-not-found`; both cases look the same.

## Dates and times

* Timestamps are RFC 3339 in UTC with `Z`, at millisecond precision: no fraction when the milliseconds are zero (`2026-11-01T00:00:00Z`), otherwise three digits (`2026-10-21T17:42:10.120Z`). Accept both shapes.
* Timestamps you send need an offset (`Z` or `+01:00`); they are converted to UTC. Without an offset → field code `invalid-timestamp`.
* Dates are `YYYY-MM-DD`, months `YYYY-MM`. Calendar periods (statistics) use your partner's `timezone`.
* Durations are integers named by unit, for example `gracePeriodMinutes`.
* Time ranges are half-open: `[from, to)`, filtered with `gte` and `lt`.

## Money

All amounts are in euros (EUR). `value` is in cent.

Money is an object with the integer `value` and a `label` formatted in the response locale (`de` `5,00 €`, `en` `€5.00`):

```json theme={null}
{ "value": 500, "label": "5,00 €" }
```

Send `{"value": 500}`; a `label` in a request is ignored. Amounts are never floats.

## Localised text

Texts that consumers see are maps from locale to text, for example `"name": {"de": "5 € Fahrguthaben", "en": "€5 ride credit"}`. Every text you send must include your partner's `defaultLocale`; supported locales are `de` and `en`. Responses always contain every locale you stored; pick the one you need client-side.

Long texts (`description`, `termsText`) are restricted Markdown: paragraphs, line breaks, bold, italic, lists, level 3-4 headings and https links. Render them with a sanitising Markdown renderer, never as raw HTML. See [Content rules](https://docs-provider.voucher.allride.io/provider/content-rules.md).

## Large integers

Ids and counters such as `maxRedemptions`, `redemptionCount`, `remaining` and `used` are unsigned 64-bit integers and **may exceed 2^53**. JavaScript's `JSON.parse` loses precision above that: use a BigInt-safe parser (for example `json-bigint` or `lossless-json`). In OpenAPI these fields have `format: uint64`.

## Updating resources

* `PATCH` is a JSON Merge Patch: absent fields stay unchanged, `null` clears a field, objects merge. Lists are replaced whole.
* `PUT` replaces a whole document (for example the stock alert rule).
* An update only writes the fields you changed, so two clients that change different fields of the same resource both succeed.
* `PATCH /provider/v1/voucher-sets/{id}` with `{}` returns the set unchanged.

## Concurrent changes

There is no optimistic locking: you send no precondition header and no revision number with an update, and the API never rejects a write because someone else changed the resource since you read it.

* **Last writer wins, per field.** A `PATCH` or `PUT` writes only the fields that change. When two clients change the same field at the same time, the later write wins; when they change different fields of the same resource, both changes survive. For example, one client renaming a voucher set while another changes its `maxRedemptions` both succeed, and the set ends up with the new name and the new limit.
* **State changes are checked when they run.** Commands (`:activate`, `:submit`, `:revoke`, …) and edits that are only allowed in some statuses check the resource's current status inside the request. If another change got there first, you get the operation's own **409**, for example `invalid-state-transition` (the voucher set is no longer a draft, or was already activated), or `voucher-set-archived`. These 409s are final for that request: read the resource again and decide whether the command still makes sense. Retrying unchanged gives the same answer.
* **Change requests can go stale.** When AllRide corrects a live voucher set while your change request for it is open, the request is based on an older version and shows `isStale: true`. AllRide cannot approve it (approval answers 409 `change-request-stale` on AllRide's side) until you edit it: any `PATCH` of the request, even only `providerNote`, rebases it on the current content. See [Change requests](https://docs-provider.voucher.allride.io/provider/change-requests.md).
* **409 `concurrent-update`** means the request collided with another one on the same data and AllRide could not apply it after retrying internally. Nothing was written: retry it with backoff (with the same `Idempotency-Key` for a `POST`, see [Idempotency](https://docs-provider.voucher.allride.io/guides/idempotency.md)).
* If several people or systems edit the same resource, read it right before you change it, and agree who owns which fields.

## Commands and batch operations

* Actions that are not plain updates are **commands**: `POST …/{id}:verb`, for example `:activate`, `:submit`, `:revoke`. Most take an empty body.
* Bulk operations use `POST …:batch` (or `…/vouchers:revoke`) and are **all or nothing**: one invalid item fails the whole request and nothing is written. Item errors are listed in `more.validationErrors`: `property` names the item (`vouchers[17].value`) and `constraint` the reason.
* A command not allowed in the resource's status → 409 `invalid-state-transition`.

## Asynchronous jobs

Voucher uploads run asynchronously:

1. The command answers **202** with `Location` (the job URL), `Retry-After: 2` and the job in its initial status.
2. Poll `Location`, starting after `Retry-After` and doubling the interval up to 30 seconds.
3. The job's `status` ends in a terminal value (`completed`, `failed`, `cancelled`), with `failureReason` on failure.

An idempotent replay of a 202 shows the job as it was when accepted; always follow `Location` for the current state. Uploads also send the `voucher-upload-completed` and `voucher-upload-failed` webhooks.

## Compatibility rules

Within `v1` AllRide adds, but never removes or changes. Your client must:

* ignore unknown response properties, `_links` relations and error members;
* treat enums as open sets and handle unknown values (for example a new `status`);
* not depend on the order of properties or of list items beyond the documented sort.

See [Versioning and deprecation](https://docs-provider.voucher.allride.io/guides/versioning.md).

## Related

* [Pagination, filtering and sorting](https://docs-provider.voucher.allride.io/guides/pagination.md)
* [Idempotency](https://docs-provider.voucher.allride.io/guides/idempotency.md)
* [Errors](https://docs-provider.voucher.allride.io/errors.md)


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