Skip to main content
Every operation of the Consumer API (/consumer/v1) and the Workspace API (/partner/v1) follows the conventions on this page. Learn them once and every reference page reads the same way.

Format

  • Requests and responses are JSON in UTF-8. Send Content-Type: application/json with every request that has a body.
  • The API is HTTPS only (TLS 1.2 or later). Production is https://api.voucher.allride.io, the sandbox is https://api.voucher-staging.allride.io.
  • Property names are camelCase; enum values are kebab-case (deactivation-scheduled).
  • Nullable properties are always present. A value that is not set is null, never a missing key, so you can rely on the shape of every resource.
  • Send Accept-Language: de or en to choose the language of server-formatted text such as money labels and error messages. The response carries Content-Language.
  • Every response carries X-Request-Id. You can send your own X-Request-Id (1-100 characters of letters, digits and ._:-) to correlate requests with your logs.

Response envelopes

A response that returns one resource wraps it in data:
A list returns a page inside data:
The other shapes: Every resource has id, resource (its type, for example redemption or redemption-link) and _links. _links maps a relation name to {href, type}; self is always present. A link appears only when your key or user holds the permissions to follow it. Value objects and statistics rows (for example analytics rows) carry resource but no id or _links.

Identifiers

  • Ids are positive integers ("id": 42), unique per resource type. They are JSON numbers in bodies and plain numbers in paths (/consumer/v1/redemptions/6017).
  • An id that is not a positive integer, in the path, the query or the body, is answered with 400 data-failed-validation; the entry in more.validationErrors has the constraint invalid-id and names the parameter or property.
  • A well-formed id that does not exist or belongs to another partner is answered with 404 and the resource’s not-found code, for example redemption-not-found. You never see another partner’s data, not even its existence.
  • Your own references use separate fields: refId on redemption entities (1-128 characters of letters, digits and ._:@/+=-) and externalRef on catalog settings (at most 64 characters). Never put personal data into them.

Dates and times

  • Timestamps are RFC 3339 in UTC with Z, at millisecond precision, truncated: no fraction when the milliseconds are zero (2026-11-01T00:00:00Z), otherwise exactly three digits (2026-10-21T17:42:10.120Z). Accept both shapes.
  • Timestamps you send must carry an offset (Z or +01:00); they are normalised to UTC. 2026-12-31T23:59:59+01:00 is stored and returned as 2026-12-31T22:59:59Z. A timestamp without an offset fails with the constraint invalid-timestamp.
  • Dates are YYYY-MM-DD, months YYYY-MM.
  • Calendar periods (analytics months and days, per-entity monthly limits) are evaluated in your partner timezone, an IANA id such as Europe/Berlin.
  • Exception for billing: billing service months and billing dates (issueDate, dueDate, collectionDate) are calendar months and dates in Europe/Berlin, whatever your partner timezone. Billing resources that carry a month also carry billingZone: "Europe/Berlin". A redemption at 2026-10-31T23:30:00Z by a partner in Europe/London counts in analytics month 2026-10 and in billing service month 2026-11. See Billing.
  • Durations are integers whose unit is in the property name: gracePeriodMinutes, expiresInMinutes, paymentTermsDays.
  • Intervals are half-open: [from, to). Filter ranges with gte and lt.

Money

All amounts are in euros (EUR). value is in cent. Money is an object with the amount in cent and a formatted label:
  • value is an integer, never a float. label is formatted in the response language (€29.00 in English, 29,00 € in German).
  • In requests send {"value": 2900}. A label you send is accepted and ignored, so you can send back what you read.
  • Amounts that can be negative (Stornorechnungen, balances) use the same shape with a signed value, for example {"value": -1250, "label": "-12,50 €"}.
  • Per-redemption unit prices in billing and pricing add a scale property: {value, scale, label} with scale always 4 and value in ten-thousandths of a euro. 1000 is €0.10 and 1250 is €0.1250:
Totals on invoices are always ordinary money in cent. Tax rates are decimal strings with two fraction digits ("19.00"), never JSON numbers. See Billing and Prices and price acceptance.

Localised text

Texts that providers and partners write per language (voucher set names, terms, landing texts) are objects keyed by locale:
  • Responses always return the full map. Pick the locale for your customer on your side.
  • When you send localised text, include your partner’s default locale.
  • Rich text fields use a restricted Markdown subset (paragraphs, line breaks, bold, italic, lists, headings level 3-4 and https links). Render them with a sanitising Markdown renderer, never as raw HTML.

Large integers

Ids and large counters (maxRedemptions, redemptionCount) are unsigned 64-bit integers. They are JSON numbers and can exceed 2^53, the largest integer a JavaScript number represents exactly. In JavaScript, parse responses with a BigInt-safe JSON parser (for example json-bigint) or keep these values as strings. In the OpenAPI document these fields have format: uint64; configure your client generator to map them to a 64-bit or arbitrary-precision type.

Updating resources

  • PATCH is a JSON Merge Patch: a property you leave out stays unchanged, null clears it, any other value replaces it.
  • PUT replaces the whole resource (for example PUT /partner/v1/smtp-configuration).
  • DELETE answers 204 without a body.
  • Request bodies are strict: a property the operation does not define is refused with the constraint unknown-field, and an undeclared query parameter with unknown-parameter. Read-only properties such as id or label in money are the only values you may echo back.

Concurrent changes

Updates are last-writer-wins per field. Resources carry no version number, responses carry no entity tag, and there are no conditional request headers: you never send a precondition with a write.
  • An update writes only the properties you sent. Two clients that change different properties of the same resource both succeed, and neither overwrites the other.
  • When two clients change the same property at the same time, the later write wins. If you need one writer, coordinate it on your side.
  • A command that changes the state checks the current state inside the same transaction. If another request changed it in the meantime, the command answers 409 with a code that names the state it found (a state race). On this site these are, for example: Read the resource again before you decide what to do; retrying the same request gives the same answer.
  • If the database could not complete your write because of a concurrent one (a deadlock or a serialization failure), you get 409 concurrent-update; retry with backoff and the same Idempotency-Key. This is the only transient conflict code; there is no per-resource concurrency error.

Commands and batch operations

  • Actions that are not plain updates are commands: POST to the resource path with a colon and a verb, for example POST /consumer/v1/redemptions/{redemptionId}:void or POST /consumer/v1/redemption-links/{redemptionLinkId}:revoke.
  • Bulk operations are POST to the collection with :batch, for example POST /consumer/v1/redemptions:batch. Batches are all or nothing: if one item fails, nothing is created, and the error names the item (items[2]) in more.validationErrors.
  • Batch results use the single-object envelope around one result resource whose items keep the order of your request.
  • Every POST, commands and batches included, accepts an Idempotency-Key.

Asynchronous jobs

No operation on the consumer site runs as an asynchronous job: every request answers when its work is done, and there are no export jobs (to get a whole list, see Downloading large lists). Asynchronous jobs exist only on the provider site (voucher uploads) and follow this pattern:
1

Start the job

The command answers 202 Accepted with the job in its initial status, a Location header with the job URL and Retry-After: 2.
2

Poll the job

GET the Location URL after Retry-After seconds. Double the interval on each poll, up to 30 seconds.
3

Use the result

When the job’s status is final, read its result.
An idempotent replay of the 202 shows the job as it was when it was accepted. Always follow Location for the current state.

Compatibility rules

Within v1 the API changes only in backwards-compatible ways (see Versioning and deprecation). To keep working when it does, your client must:
  • ignore response properties and _links relations it does not know;
  • treat enum values it does not know as “other” (every enum is an open set);
  • ignore members of an error entry or its more object that it does not know;
  • ignore webhook event types it has not subscribed to or does not know.
When you contact support about a failed request, include the X-Request-Id of the response (for server errors it is also in requestId).