Skip to main content

Vouchers

A voucher is one redeemable item of a set: a code (voucherKind: code) or a link (voucherKind: link). Voucher status: available, exhausted (all redemptions used), expired (past validUntil) or revoked. Links are checked against the set’s current content: the live revision, or the draft or pending content if the set was never approved. You can add vouchers in every status except suspended and archived; no change request is needed. New vouchers are redeemable once the set is active. Stock figures update within about two minutes.

Upload a file

For more than 1,000 vouchers, upload a CSV or XLSX file. The file goes directly to storage, never through the API:
1

Create the upload

The answer is 201 with status: pending-upload and an uploadTarget (url, method, headers, expiresAt), valid for 1 hour.
2

PUT the file to the upload target

Send exactly the method and headers of uploadTarget (text/csv, or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for XLSX).
3

Start processing

202 with Location and Retry-After: 2. A missing file → 409 upload-file-missing; a size that differs from sizeBytes → 422 upload-size-mismatch.
4

Wait for the result

Poll Location (start after 2 seconds, double up to 30 seconds) or wait for the voucher-upload-completed / voucher-upload-failed webhook. You also get an email.
At most 10 uploads per set can be open at a time (409 voucher-upload-limit-reached). An upload not started within 1 hour is cancelled with failureReason: upload-expired. Cancel an upload in pending-upload or validated with :cancel.

File format

  • CSV: UTF-8 (a BOM is allowed), comma or semicolon separator (detected from the header), RFC 4180 quoting, LF or CRLF line ends.
  • XLSX: the first worksheet only; strings, numbers and date cells (read as UTC).
  • At most 100 MiB and 1,000,000 data rows.
  • A header row is required. Column names are case-insensitive: value (required), redemptionType, maxRedemptions, validFrom, validUntil, metadata (a JSON object as text).
  • An empty cell takes the upload’s defaults. In maxRedemptions, the word unlimited means unlimited, even when the defaults set a maximum.
A file without a value column fails with failureReason: missing-value-column. An unknown column fails the whole upload with unknown-column before any row is checked, so a misspelt validUntill never silently drops data. A row with more or fewer cells than the header is invalid (column-count-mismatch).

Validation and import

Processing has two phases:
  1. Validation. The file’s SHA-256 is checked (checksum-mismatch fails the upload), then every row is validated before any voucher is written.
  2. Import. With no invalid rows, the import starts automatically. With invalid rows, the upload stops in validated: inspect the errors, then confirm with :import and {"skipInvalidRows": true} to import only the valid rows, or :cancel. A validated upload left for 7 days is cancelled (validation-expired).
Row errors are {rowNumber, column, code} (never the value); up to 10,000 are stored. Codes: value-missing, value-invalid, link-host-not-allowed, scheme-not-allowed, redemption-type-invalid, max-redemptions-invalid, max-redemptions-not-allowed-for-single, max-redemptions-too-small, validity-window-invalid, date-invalid, metadata-invalid, column-count-mismatch. Duplicates are not errors. A value repeated in the file (duplicate-in-file) or already in the set (value-exists) is a valid row that is skipped and counted in duplicateRows; it appears in the row list but never stops the upload. Uploading the same file twice imports nothing the second time. counts follows: totalRows = validRows + invalidRows, and when completed importedRows = validRows − duplicateRows. Imports run in chunks of 5,000 rows and resume after interruptions without duplicates. A row whose validUntil passed between validation and import is skipped as validity-window-invalid. If AllRide suspends or you archive the set during an import, the upload fails with voucher-set-unavailable; chunks already imported stay.

Batch API

Create up to 1,000 vouchers synchronously, all or nothing:
  • 201 with {createdCount, items}: compact items (id, valueMask, redemptionType, maxRedemptions, status) in request order.
  • Any invalid item → 400 data-failed-validation with one entry per problem (vouchers[17].value); nothing is created.
  • A value already in the set → 409 voucher-value-exists, naming each item; a value twice in the request → duplicate-value. Nothing is created.
  • More than 1,000 items → 422 batch-too-large.
  • Send an Idempotency-Key: a retry then replays the result and never duplicates vouchers.

Revoking vouchers

Revoke vouchers that must never be redeemed (for example leaked codes):
  • Only available vouchers can be revoked; others → 409 voucher-not-revocable. Up to 1,000 ids, all or nothing.
  • One voucher: POST /provider/v1/vouchers/{voucherId}:revoke with {"reason": …}.
  • Revoked vouchers are never redeemable and never deleted; past redemptions stay.

Viewing voucher values

List and get responses always show masked values (valueMask: the first 2 and last 2 characters of a code, scheme and host of a link), for every principal. Dashboard downloads (CSV / Excel) contain the same masked values. The clear value of one voucher comes only from the reveal operation (reveal-provider-vouchers, delegable; dashboard users need a sign-in within the last 15 minutes):
  • One voucher per call; there is no bulk reveal.
  • Every reveal is recorded in your audit log as voucher.revealed (never with the value) and monitored by AllRide.
  • An idempotent replay returns "value": null with Idempotent-Replay-Redacted: true.
  • To find a voucher by its value, use search on the vouchers list: an exact match on the full value, available only to principals with reveal-provider-vouchers; results stay masked.