From d85eff2e95910e51c0e064e3db40ac03447a7a47 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 15:16:32 +0200 Subject: [PATCH 01/17] Clarify Redemption API response shapes Restructure the Redemption API page so readers can tell which fields are populated for each kind of e_code. The previous version described e_code as a single flat shape and incorrectly claimed pin contains the URL for url-type products. - Split response docs into three explicit shapes (plain code, barcode, url) with a summary table - Fix the pin/url field error - Document that code duplicates url for url-type products - Note that e_code.status mirrors the top-level status - Pull rate-limiting note out of the format param into its own callout - State that the payout URL itself is the credential - Replace the single example with a CodeGroup covering all three success shapes plus the error case Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 277 +++++++++++++++++++++++++----------- 1 file changed, 197 insertions(+), 80 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 2c61737..e7c3ec9 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -5,21 +5,26 @@ icon: "code" --- - The Redemption API is not available by default. To enable this feature, - contact your Account Manager. + The Redemption API is not available by default. To enable it, contact your + Account Manager. The same feature gate applies to both the + [Playground](/reference/2024-02-05/playground) and Production environments. -The Redemption API allows you to retrieve raw gift card redemption details at the point where you need to display them to your customers. This gives you control over the customer journey by presenting gift card details within your own experience, rather than using the default Runa redemption page. - -## Availability - -The Redemption API is available in both the [Playground](/reference/2024-02-05/playground) and Production environments. The same feature gate applies to both — contact your Account Manager to enable it on your account before testing in Playground. +The Redemption API lets you fetch raw gift card redemption details — codes, PINs, barcodes, or redemption URLs — so you can present them inside your own customer experience instead of redirecting customers to the default Runa redemption page. ## How it works 1. [Create an order](/reference/2024-02-05/endpoint/orders/create) as usual. -2. The order response contains a `url` field in the `payout` object. -3. Append `?format=json` to the URL to retrieve the redemption details in a structured format. +2. Read the `url` field from the `payout` object in the order response. +3. Append `?format=json` to that URL and `GET` it to receive structured redemption data. + +The URL itself is the credential — it is unique per payout and grants access to that payout's redemption details. No additional API key or header is required. + + + Without `?format=json`, the URL returns the default Runa redemption web page + and is rate limited. The `json` format is intended for server-to-server use + and must be enabled by your Account Manager. + ## Request @@ -30,144 +35,256 @@ GET {payout.url}?format=json ### Query parameters - Response format. Set to `json` to receive structured gift card data. The - `json` format must be enabled by your Account Manager. Rate limiting applies - when using `html`. + Set to `json` to receive structured gift card data. Omit, or set to `html`, + to receive the default Runa redemption page. ## Response +The wrapper is the same for every successful response: + - `SUCCESS` or `ERROR` If `ERROR`, check the `error_code` and `error_string` - fields for details. If `SUCCESS`, the gift card redemption details are in the - `e_code` field. + `SUCCESS` or `ERROR`. On `ERROR`, see the top-level `error_code` and + `error_string`. On `SUCCESS`, redemption details are in `e_code`. Your order ID (e.g. `O-ABC123`). + + Top-level error code, or `null`. See [Error codes](#error-codes). + + + + Human-readable description of the top-level error, or `null`. + + + + Additional error details, or `null`. + + - The gift card redemption details. + The gift card redemption details. The populated fields depend on + `e_code.type` — see [Response shapes](#response-shapes) below. + - +## Response shapes + +The `e_code.type` field tells you how to render the gift card. There are three shapes in practice: + +| `type` | When to use it | Render as | +| -------- | ---------------------------------------------- | ---------------------------------- | +| `code` | Plain alphanumeric code (with optional PIN) | Display `code` (and `pin` if set) | +| `code` | Barcode-backed code | Display `code` and render a barcode from `barcode_string` using `barcode_format` | +| `url` | Redemption hosted by the brand | Redirect the customer to `url` | + +### Common `e_code` fields + +These fields are present on every shape: - `code` or `url` For `code` type products, gift card details are in the `code`, - `pin`, etc. fields. For `url` type products, redirect your customer to the - `url` field. + `code` or `url`. Determines which other fields are populated. + + + + Mirrors the top-level `status`. Always equal to it — provided for backwards + compatibility. + + + + Amount in major units, as a decimal string (e.g. `"50.00"`). + + + + Expiry date of the gift card. The sentinel value `3000-01-01` indicates the + gift card does not expire. + + + + A URL where the customer can access their gift card. Always present. + + Per-`e_code` error code, or `null`. See [Error codes](#error-codes). + + + + Per-`e_code` error description, or `null`. + + +### Shape 1 — `type: "code"`, plain code + +The customer redeems by entering the `code` (and `pin`, if present) at the brand. Barcode fields are `null`. + - The primary gift card code, if applicable. + The gift card code. - The gift card PIN, if applicable. For `url` type products, this contains the - URL. + PIN, if the brand requires one. `null` otherwise. Only present on plain-code + redemptions — never set alongside a barcode. - - URL where your customer can access their gift card. + + Always `null` for this shape. - - Amount in major units as a decimal string. + + Always `null` for this shape. - - Expiry date of the gift card, if applicable. `3000-01-01` indicates the gift - card is valid indefinitely. +### Shape 2 — `type: "code"`, barcode + +The customer redeems by scanning a barcode in-store. `barcode_format` and `barcode_string` are always set together — you need the format to decode the string. `pin` is always `null` for this shape. + + + The gift card code (also encoded in the barcode). - Barcode format: `code-128`, `code-39`, `ean-13`, `QR`, or `pdf417`. Also - available from the [product details + One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`. Use this to choose + the right barcode renderer. Also returned by the [product details endpoint](/reference/2024-02-05/endpoint/products/list). - The barcode value. Encode using `barcode_format` to produce a valid barcode. + The value to encode. Pass through your barcode library using + `barcode_format`. - - Error code if the product was not retrieved. + + Always `null` for this shape. - - Description of the error if the product was not retrieved. - - +### Shape 3 — `type: "url"` + +The brand hosts redemption on their own site. Redirect the customer to `url`. There is no code to display. + + + Redirect the customer here to redeem. - - Top-level error code, or `null`. + + Duplicate of `url` — kept for backwards compatibility. Prefer `url`. - - Description of the error, or `null`. + + Always `null` for this shape. - - Additional error details, or `null`. + + Always `null` for this shape. -## Error codes + + Always `null` for this shape. + -| Code | Description | -| ------- | -------------------------------------------------------------------------------------------------------------- | -| `RE001` | The e-code has not been found. Please check the link. | -| `RE002` | This account has not been activated or is no longer active. Please contact Runa. | -| `RE003` | The e-code has been invalidated. Please contact Runa. | -| `RE004` | The e-code has expired. You may need to place a new order. | -| `RE005` | The e-code has been cancelled. | -| `RE006` | The e-code could not be redeemed at this time as there was no product stock available. Please try again later. | -| `RE007` | The e-code could not be redeemed at this time as the product is not active. Please contact Runa. | -| `RE008` | A processing error occurred. Please check the status page and try again later. | -| `RE009` | The request was placed too soon after a processing error. Please wait at least 1 minute before retrying. | -| `RE010` | Your account is out of funds. Please check your account balance before trying again. | -| `RE011` | The token was not found. Please check if your token is correct. | -| `RE012` | The order item is invalid. The order item may have been cancelled or refunded. | -| `RE013` | Feature not enabled. | -| `RE999` | An unexpected error occurred. Please contact Runa or try again later. | +## Examples - -```http Request -GET https://spend.runa.io/-/abc123…def?format=json + +```json Plain code +{ + "status": "SUCCESS", + "order_id": "O-XW2BQAM2", + "e_code": { + "type": "code", + "status": "SUCCESS", + "amount": "1.00", + "code": "WE3K-XTLHHZ-EKAL", + "pin": null, + "barcode_format": null, + "barcode_string": null, + "expiry_date": "2036-02-26", + "url": "https://spend.runa.io/-/88333b9f-ce8d-48f6-b851-eedd401cc3d2", + "error_code": null, + "error_string": null + }, + "error_code": null, + "error_string": null, + "error_details": null +} ``` - - -```json Success response +```json Barcode { "status": "SUCCESS", "order_id": "O-QRXNZBJV", "e_code": { + "type": "code", + "status": "SUCCESS", "amount": "50.00", + "code": "6280399992147003", + "pin": null, "barcode_format": "QR", "barcode_string": "54390399548343003", - "code": "6280399992147003", - "error_code": null, - "error_string": null, "expiry_date": "2025-02-28", - "pin": "25468113", + "url": "https://spend.runa.io/-/9b98244b-ab9b-4bff-b5f6-7777786b9f62", + "error_code": null, + "error_string": null + }, + "error_code": null, + "error_string": null, + "error_details": null +} +``` + +```json URL +{ + "status": "SUCCESS", + "order_id": "O-N5PX9YBW", + "e_code": { + "type": "url", "status": "SUCCESS", - "type": "code", - "url": "https://spend.runa.io/-/9b98244b-ab9b-4bff-b5f6-7777786b9f62" + "amount": "10.00", + "code": "https://spend.runa.io/-/68802d8e-3e58-41dd-aea7-9e472debf656", + "pin": null, + "barcode_format": null, + "barcode_string": null, + "expiry_date": "2028-04-30", + "url": "https://spend.runa.io/-/68802d8e-3e58-41dd-aea7-9e472debf656", + "error_code": null, + "error_string": null }, "error_code": null, - "error_details": null, - "error_string": null + "error_string": null, + "error_details": null } ``` -```json Error response +```json Error { "status": "ERROR", "order_id": "O-QRXNZBJV", "error_code": "RE004", - "error_details": "Asset has expired", - "error_string": "The e-code has expired. You may need to place a new order." + "error_string": "The e-code has expired. You may need to place a new order.", + "error_details": "Asset has expired" } ``` - + + +## Error codes + +| Code | Description | +| ------- | -------------------------------------------------------------------------------------------------------------- | +| `RE001` | The e-code has not been found. Please check the link. | +| `RE002` | This account has not been activated or is no longer active. Please contact Runa. | +| `RE003` | The e-code has been invalidated. Please contact Runa. | +| `RE004` | The e-code has expired. You may need to place a new order. | +| `RE005` | The e-code has been cancelled. | +| `RE006` | The e-code could not be redeemed at this time as there was no product stock available. Please try again later. | +| `RE007` | The e-code could not be redeemed at this time as the product is not active. Please contact Runa. | +| `RE008` | A processing error occurred. Please check the status page and try again later. | +| `RE009` | The request was placed too soon after a processing error. Please wait at least 1 minute before retrying. | +| `RE010` | Your account is out of funds. Please check your account balance before trying again. | +| `RE011` | The token was not found. Please check if your token is correct. | +| `RE012` | The order item is invalid. The order item may have been cancelled or refunded. | +| `RE013` | Feature not enabled. | +| `RE999` | An unexpected error occurred. Please contact Runa or try again later. | + + +```http Request +GET https://spend.runa.io/-/abc123…def?format=json +``` + From f42953629794dcf6b0998c539d0877a90550aa2b Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 15:23:34 +0200 Subject: [PATCH 02/17] Reword Response section intro Replace "The wrapper is the same for every successful response" with plainer language describing what the top-level fields are and pointing to the per-type variation in e_code. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index e7c3ec9..da054dd 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -41,7 +41,7 @@ GET {payout.url}?format=json ## Response -The wrapper is the same for every successful response: +The top-level fields below are present on every response. The contents of `e_code` depend on the gift card `type` — see [Response shapes](#response-shapes). `SUCCESS` or `ERROR`. On `ERROR`, see the top-level `error_code` and From d78bdd215181e025f204dea1e4f2cf30bd7b0039 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 15:24:22 +0200 Subject: [PATCH 03/17] =?UTF-8?q?Fix=20barcode=20shape=20=E2=80=94=20code?= =?UTF-8?q?=20is=20null,=20not=20populated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For barcode-backed redemptions the API returns no code field — only barcode_format and barcode_string are set, and the customer redeems by scanning the rendered barcode. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index da054dd..8814615 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -76,7 +76,7 @@ The `e_code.type` field tells you how to render the gift card. There are three s | `type` | When to use it | Render as | | -------- | ---------------------------------------------- | ---------------------------------- | | `code` | Plain alphanumeric code (with optional PIN) | Display `code` (and `pin` if set) | -| `code` | Barcode-backed code | Display `code` and render a barcode from `barcode_string` using `barcode_format` | +| `code` | Barcode-backed redemption | Render a barcode from `barcode_string` using `barcode_format` (no `code` is returned) | | `url` | Redemption hosted by the brand | Redirect the customer to `url` | ### Common `e_code` fields @@ -136,11 +136,7 @@ The customer redeems by entering the `code` (and `pin`, if present) at the brand ### Shape 2 — `type: "code"`, barcode -The customer redeems by scanning a barcode in-store. `barcode_format` and `barcode_string` are always set together — you need the format to decode the string. `pin` is always `null` for this shape. - - - The gift card code (also encoded in the barcode). - +The customer redeems by scanning a barcode in-store. Render the barcode from `barcode_string` using `barcode_format` — those two fields are always set together, since you need the format to decode the string. `code` and `pin` are always `null` for this shape. One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`. Use this to choose @@ -153,6 +149,10 @@ The customer redeems by scanning a barcode in-store. `barcode_format` and `barco `barcode_format`. + + Always `null` for this shape. + + Always `null` for this shape. @@ -215,7 +215,7 @@ The brand hosts redemption on their own site. Redirect the customer to `url`. Th "type": "code", "status": "SUCCESS", "amount": "50.00", - "code": "6280399992147003", + "code": null, "pin": null, "barcode_format": "QR", "barcode_string": "54390399548343003", From ec7c88110eec75e243bb5bc94539d588292bc72c Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 15:31:18 +0200 Subject: [PATCH 04/17] Rework type:code as orthogonal code/pin/barcode fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous structure split type:code into "plain code" and "barcode" shapes with mutually exclusive fields, but in reality code, pin, and barcode are independent — six combinations are valid. List the combinations and tell integrators to render whatever fields are non-null instead of branching on the combination. Update the second example to show code + pin + barcode together so readers can see all three coexisting. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 72 +++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 8814615..04fb39c 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -71,13 +71,17 @@ The top-level fields below are present on every response. The contents of `e_cod ## Response shapes -The `e_code.type` field tells you how to render the gift card. There are three shapes in practice: +The `e_code.type` field tells you how to render the gift card: -| `type` | When to use it | Render as | -| -------- | ---------------------------------------------- | ---------------------------------- | -| `code` | Plain alphanumeric code (with optional PIN) | Display `code` (and `pin` if set) | -| `code` | Barcode-backed redemption | Render a barcode from `barcode_string` using `barcode_format` (no `code` is returned) | -| `url` | Redemption hosted by the brand | Redirect the customer to `url` | +| `type` | What's returned | How to render | +| -------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------- | +| `code` | Some non-empty subset of `code`, `pin`, and a barcode | Display whichever of `code`, `pin`, and the barcode are non-null | +| `url` | A redemption `url` hosted by the brand | Redirect the customer to `url` | + + + For `type: "code"`, the simplest integration is to render whatever fields + are non-null rather than branching on which combination you got back. + ### Common `e_code` fields @@ -113,51 +117,39 @@ These fields are present on every shape: Per-`e_code` error description, or `null`. -### Shape 1 — `type: "code"`, plain code +### `type: "code"` -The customer redeems by entering the `code` (and `pin`, if present) at the brand. Barcode fields are `null`. +The response includes a non-empty subset of `code`, `pin`, and a barcode (`barcode_format` + `barcode_string`). Any of the following combinations can occur: - - The gift card code. - +- `code` only +- `pin` only +- `code` + `pin` +- `code` + barcode +- `pin` + barcode +- `code` + `pin` + barcode - - PIN, if the brand requires one. `null` otherwise. Only present on plain-code - redemptions — never set alongside a barcode. - +Fields not part of the redemption are returned as `null`. To keep your integration simple, just render whatever is non-null. - - Always `null` for this shape. + + Alphanumeric gift card code, or `null`. - - Always `null` for this shape. + + PIN, or `null`. -### Shape 2 — `type: "code"`, barcode - -The customer redeems by scanning a barcode in-store. Render the barcode from `barcode_string` using `barcode_format` — those two fields are always set together, since you need the format to decode the string. `code` and `pin` are always `null` for this shape. - - One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`. Use this to choose - the right barcode renderer. Also returned by the [product details + One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`, or `null`. Use this + to choose the right barcode renderer. Also returned by the [product details endpoint](/reference/2024-02-05/endpoint/products/list). - The value to encode. Pass through your barcode library using - `barcode_format`. + The value to encode in the barcode, or `null`. Always set together with + `barcode_format` — you need the format to decode the string. - - Always `null` for this shape. - - - - Always `null` for this shape. - - -### Shape 3 — `type: "url"` +### `type: "url"` The brand hosts redemption on their own site. Redirect the customer to `url`. There is no code to display. @@ -184,7 +176,7 @@ The brand hosts redemption on their own site. Redirect the customer to `url`. Th ## Examples -```json Plain code +```json Code only { "status": "SUCCESS", "order_id": "O-XW2BQAM2", @@ -207,7 +199,7 @@ The brand hosts redemption on their own site. Redirect the customer to `url`. Th } ``` -```json Barcode +```json Code + PIN + barcode { "status": "SUCCESS", "order_id": "O-QRXNZBJV", @@ -215,8 +207,8 @@ The brand hosts redemption on their own site. Redirect the customer to `url`. Th "type": "code", "status": "SUCCESS", "amount": "50.00", - "code": null, - "pin": null, + "code": "6280399992147003", + "pin": "25468113", "barcode_format": "QR", "barcode_string": "54390399548343003", "expiry_date": "2025-02-28", From c367b395b43c1a310f3d3407b982476d0b72d1ca Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 15:33:38 +0200 Subject: [PATCH 05/17] =?UTF-8?q?Flatten=20Response=20section=20=E2=80=94?= =?UTF-8?q?=20drop=20shapes=20scaffolding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapse "Common fields" / "type: code" / "type: url" subsections into a single e_code object section with a short prose summary at the top that covers the rendering decision. Drop the table, the bulleted combination enumeration, and the per-type ResponseField repetition — the examples carry that load. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 77 ++++++++----------------------------- 1 file changed, 15 insertions(+), 62 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 04fb39c..6a73679 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -65,35 +65,21 @@ The top-level fields below are present on every response. The contents of `e_cod - The gift card redemption details. The populated fields depend on - `e_code.type` — see [Response shapes](#response-shapes) below. + The gift card redemption details. See [`e_code` object](#e-code-object) + below. -## Response shapes +## `e_code` object -The `e_code.type` field tells you how to render the gift card: +The `type` field determines how to render the gift card: -| `type` | What's returned | How to render | -| -------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------- | -| `code` | Some non-empty subset of `code`, `pin`, and a barcode | Display whichever of `code`, `pin`, and the barcode are non-null | -| `url` | A redemption `url` hosted by the brand | Redirect the customer to `url` | +- **`type: "code"`** — display whichever of `code`, `pin`, `barcode_format`, and `barcode_string` are non-null. The brand decides which subset is populated; render whatever is available rather than branching on the combination. +- **`type: "url"`** — redirect the customer to `url`. The `code` field duplicates `url` (kept for backwards compatibility); `pin` and the barcode fields are always `null`. - - For `type: "code"`, the simplest integration is to render whatever fields - are non-null rather than branching on which combination you got back. - - -### Common `e_code` fields - -These fields are present on every shape: +See [Examples](#examples) for representative payloads. - `code` or `url`. Determines which other fields are populated. - - - - Mirrors the top-level `status`. Always equal to it — provided for backwards - compatibility. + `code` or `url`. @@ -109,29 +95,8 @@ These fields are present on every shape: A URL where the customer can access their gift card. Always present. - - Per-`e_code` error code, or `null`. See [Error codes](#error-codes). - - - - Per-`e_code` error description, or `null`. - - -### `type: "code"` - -The response includes a non-empty subset of `code`, `pin`, and a barcode (`barcode_format` + `barcode_string`). Any of the following combinations can occur: - -- `code` only -- `pin` only -- `code` + `pin` -- `code` + barcode -- `pin` + barcode -- `code` + `pin` + barcode - -Fields not part of the redemption are returned as `null`. To keep your integration simple, just render whatever is non-null. - - Alphanumeric gift card code, or `null`. + Alphanumeric gift card code, or `null`. For `type: "url"`, duplicates `url`. @@ -149,28 +114,16 @@ Fields not part of the redemption are returned as `null`. To keep your integrati `barcode_format` — you need the format to decode the string. -### `type: "url"` - -The brand hosts redemption on their own site. Redirect the customer to `url`. There is no code to display. - - - Redirect the customer here to redeem. - - - - Duplicate of `url` — kept for backwards compatibility. Prefer `url`. - - - - Always `null` for this shape. + + Mirrors the top-level `status`. Provided for backwards compatibility. - - Always `null` for this shape. + + Per-`e_code` error code, or `null`. See [Error codes](#error-codes). - - Always `null` for this shape. + + Per-`e_code` error description, or `null`. ## Examples From 6037c96628bd21cc20649859d20f64aa9af34961 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 16:59:34 +0200 Subject: [PATCH 06/17] Fix broken anchor link to e_code object section The intro pointed at #response-shapes, but that heading was renamed to "e_code object" in the previous commit. Update the anchor to match. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 6a73679..94a01ac 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -41,7 +41,7 @@ GET {payout.url}?format=json ## Response -The top-level fields below are present on every response. The contents of `e_code` depend on the gift card `type` — see [Response shapes](#response-shapes). +The top-level fields below are present on every response. The contents of `e_code` depend on the gift card `type` — see [`e_code` object](#e-code-object). `SUCCESS` or `ERROR`. On `ERROR`, see the top-level `error_code` and From 5f36e437c1a5b13304b4a1fdb13890d75ed1940d Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 17:01:02 +0200 Subject: [PATCH 07/17] Merge e_code ResponseField into section intro The e_code ResponseField description was just a "see below" pointer to the section that immediately followed. Drop the ResponseField and lead the section with the description that was on it. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 94a01ac..133ac2f 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -64,14 +64,9 @@ The top-level fields below are present on every response. The contents of `e_cod Additional error details, or `null`. - - The gift card redemption details. See [`e_code` object](#e-code-object) - below. - - ## `e_code` object -The `type` field determines how to render the gift card: +The gift card redemption details. The `type` field determines how to render the gift card: - **`type: "code"`** — display whichever of `code`, `pin`, `barcode_format`, and `barcode_string` are non-null. The brand decides which subset is populated; render whatever is available rather than branching on the combination. - **`type: "url"`** — redirect the customer to `url`. The `code` field duplicates `url` (kept for backwards compatibility); `pin` and the barcode fields are always `null`. From 9ae14811745124e1f08362b4b8646fbbb166c511 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 17:35:35 +0200 Subject: [PATCH 08/17] =?UTF-8?q?Fix=20e=5Fcode=20object=20anchor=20?= =?UTF-8?q?=E2=80=94=20preserve=20underscore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mintlify preserves underscores in heading slugs, so "e_code object" slugs to e_code-object, not e-code-object. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 133ac2f..aadc32a 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -41,7 +41,7 @@ GET {payout.url}?format=json ## Response -The top-level fields below are present on every response. The contents of `e_code` depend on the gift card `type` — see [`e_code` object](#e-code-object). +The top-level fields below are present on every response. The contents of `e_code` depend on the gift card `type` — see [`e_code` object](#e_code-object). `SUCCESS` or `ERROR`. On `ERROR`, see the top-level `error_code` and From 2b4d648516a2af7e86c15a3fbd2c6b87a8c2bc22 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 17:37:09 +0200 Subject: [PATCH 09/17] =?UTF-8?q?Fix=20mismatched=20link=20text=20?= =?UTF-8?q?=E2=80=94=20products=20list,=20not=20details?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The link pointed at /products/list but the link text said "product details endpoint". Match the text to the destination. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index aadc32a..868d64a 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -100,7 +100,7 @@ See [Examples](#examples) for representative payloads. One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`, or `null`. Use this - to choose the right barcode renderer. Also returned by the [product details + to choose the right barcode renderer. Also returned by the [products list endpoint](/reference/2024-02-05/endpoint/products/list). From 70797e134f841425273ccc84d241f83190871de5 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 17:38:00 +0200 Subject: [PATCH 10/17] Point barcode_format reference at get product endpoint barcode_format is exposed by the get product endpoint, not the list endpoint. Update both link text and URL. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 868d64a..01668a9 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -100,8 +100,8 @@ See [Examples](#examples) for representative payloads. One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`, or `null`. Use this - to choose the right barcode renderer. Also returned by the [products list - endpoint](/reference/2024-02-05/endpoint/products/list). + to choose the right barcode renderer. Also returned by the [get product + endpoint](/reference/2024-02-05/endpoint/products/get). From 0b73b1baeb57b8a04d1486ce3b7f685db21b5a8b Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 17:43:33 +0200 Subject: [PATCH 11/17] Demote e_code from H2 section to ResponseField + Expandable Match the visual weight of the other top-level fields. The type rendering description now lives in the e_code ResponseField's body, and the child attributes are wrapped in an Expandable. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 124 +++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 58 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index bfe704d..5ce7cd1 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -41,8 +41,6 @@ GET {payout.url}?format=json ## Response -The top-level fields below are present on every response. The contents of `e_code` depend on the gift card `type` — see [`e_code` object](#e_code-object). - `SUCCESS` or `ERROR`. On `ERROR`, see the top-level `error_code` and `error_string`. On `SUCCESS`, redemption details are in `e_code`. @@ -64,62 +62,72 @@ The top-level fields below are present on every response. The contents of `e_cod Additional error details, or `null`. -## `e_code` object - -The gift card redemption details. The `type` field determines how to render the gift card: - -- **`type: "code"`** — display whichever of `code`, `pin`, `barcode_format`, and `barcode_string` are non-null. The brand decides which subset is populated; render whatever is available rather than branching on the combination. -- **`type: "url"`** — redirect the customer to `url`. The `code` field duplicates `url` (kept for backwards compatibility); `pin` and the barcode fields are always `null`. - -See [Examples](#examples) for representative payloads. - - - `code` or `url`. - - - - Amount in major units, as a decimal string (e.g. `"50.00"`). - - - - Expiry date of the gift card. The sentinel value `3000-01-01` indicates the - gift card does not expire. This reflects the product's own expiry policy and - is separate from the payout link expiry returned by the order endpoints. - - - - A URL where the customer can access their gift card. Always present. - - - - Alphanumeric gift card code, or `null`. For `type: "url"`, duplicates `url`. - - - - PIN, or `null`. - - - - One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`, or `null`. Use this - to choose the right barcode renderer. Also returned by the [get product - endpoint](/reference/2024-02-05/endpoint/products/get). - - - - The value to encode in the barcode, or `null`. Always set together with - `barcode_format` — you need the format to decode the string. - - - - Mirrors the top-level `status`. Provided for backwards compatibility. - - - - Per-`e_code` error code, or `null`. See [Error codes](#error-codes). - - - - Per-`e_code` error description, or `null`. + + The gift card redemption details. The `type` field determines how to render + the gift card: + + - **`type: "code"`** — display whichever of `code`, `pin`, `barcode_format`, + and `barcode_string` are non-null. The brand decides which subset is + populated; render whatever is available rather than branching on the + combination. + - **`type: "url"`** — redirect the customer to `url`. The `code` field + duplicates `url` (kept for backwards compatibility); `pin` and the barcode + fields are always `null`. + + See [Examples](#examples) for representative payloads. + + + + `code` or `url`. + + + + Amount in major units, as a decimal string (e.g. `"50.00"`). + + + + Expiry date of the gift card. The sentinel value `3000-01-01` indicates + the gift card does not expire. This reflects the product's own expiry + policy and is separate from the payout link expiry returned by the order + endpoints. + + + + A URL where the customer can access their gift card. Always present. + + + + Alphanumeric gift card code, or `null`. For `type: "url"`, duplicates + `url`. + + + + PIN, or `null`. + + + + One of `code-128`, `code-39`, `ean-13`, `QR`, `pdf417`, or `null`. Use + this to choose the right barcode renderer. Also returned by the [get + product endpoint](/reference/2024-02-05/endpoint/products/get). + + + + The value to encode in the barcode, or `null`. Always set together with + `barcode_format` — you need the format to decode the string. + + + + Mirrors the top-level `status`. Provided for backwards compatibility. + + + + Per-`e_code` error code, or `null`. See [Error codes](#error-codes). + + + + Per-`e_code` error description, or `null`. + + ## Examples From 0a80e547fd31cff0c7fc9d641566addd3a664109 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 18:32:15 +0200 Subject: [PATCH 12/17] Replace static JSON examples with Playground product table Swap the four hand-written JSON CodeGroup tabs for a table of test products that reproduce each response shape against the Playground. Integrators can order one of these and inspect the real response, which is more grounded than reading a synthetic payload. Verified against Playground: FIVEG-US, AUCHAN-FR, ABO-GB, LUSH-CA all return the claimed shapes consistently. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 88 ++++--------------------------------- 1 file changed, 8 insertions(+), 80 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 5ce7cd1..b0ce105 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -132,86 +132,14 @@ GET {payout.url}?format=json ## Examples - -```json Code only -{ - "status": "SUCCESS", - "order_id": "O-XW2BQAM2", - "e_code": { - "type": "code", - "status": "SUCCESS", - "amount": "1.00", - "code": "WE3K-XTLHHZ-EKAL", - "pin": null, - "barcode_format": null, - "barcode_string": null, - "expiry_date": "2036-02-26", - "url": "https://spend.runa.io/-/88333b9f-ce8d-48f6-b851-eedd401cc3d2", - "error_code": null, - "error_string": null - }, - "error_code": null, - "error_string": null, - "error_details": null -} -``` - -```json Code + PIN + barcode -{ - "status": "SUCCESS", - "order_id": "O-QRXNZBJV", - "e_code": { - "type": "code", - "status": "SUCCESS", - "amount": "50.00", - "code": "6280399992147003", - "pin": "25468113", - "barcode_format": "QR", - "barcode_string": "54390399548343003", - "expiry_date": "2025-02-28", - "url": "https://spend.runa.io/-/9b98244b-ab9b-4bff-b5f6-7777786b9f62", - "error_code": null, - "error_string": null - }, - "error_code": null, - "error_string": null, - "error_details": null -} -``` - -```json URL -{ - "status": "SUCCESS", - "order_id": "O-N5PX9YBW", - "e_code": { - "type": "url", - "status": "SUCCESS", - "amount": "10.00", - "code": "https://spend.runa.io/-/68802d8e-3e58-41dd-aea7-9e472debf656", - "pin": null, - "barcode_format": null, - "barcode_string": null, - "expiry_date": "2028-04-30", - "url": "https://spend.runa.io/-/68802d8e-3e58-41dd-aea7-9e472debf656", - "error_code": null, - "error_string": null - }, - "error_code": null, - "error_string": null, - "error_details": null -} -``` - -```json Error -{ - "status": "ERROR", - "order_id": "O-QRXNZBJV", - "error_code": "RE004", - "error_string": "The e-code has expired. You may need to place a new order.", - "error_details": "Asset has expired" -} -``` - +To see each response shape in practice, place a sync order for one of the products below in the [Playground](/reference/2024-02-05/playground), then `GET` the resulting `payout.url` with `?format=json`. + +| Product code | Order as | Returned shape | +| ------------ | --------------------- | -------------------------------------------------------- | +| `FIVEG-US` | USD, face value `5` | `type: "code"`, with `code` + `pin` | +| `AUCHAN-FR` | EUR, face value `10` | `type: "code"`, with `code` + `pin` + `code-128` barcode | +| `ABO-GB` | GBP, face value `5` | `type: "code"`, with `code` + `pin` + `QR` barcode | +| `LUSH-CA` | CAD, face value `10` | `type: "url"` | ## Error codes From ae7bfc68af7df3ef0834e5aa29c8f6ecca970b4f Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 18:35:19 +0200 Subject: [PATCH 13/17] Add success and error JSON samples back under the table Keep one representative success payload (code + pin + QR barcode, as returned by ABO-GB) and the error payload, since the error shape differs structurally from the success shape and isn't obvious from the field list alone. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index b0ce105..c013ba5 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -141,6 +141,41 @@ To see each response shape in practice, place a sync order for one of the produc | `ABO-GB` | GBP, face value `5` | `type: "code"`, with `code` + `pin` + `QR` barcode | | `LUSH-CA` | CAD, face value `10` | `type: "url"` | + +```json Success +{ + "status": "SUCCESS", + "order_id": "O-QRXNZBJV", + "e_code": { + "type": "code", + "status": "SUCCESS", + "amount": "5.00", + "code": "7756580246805369", + "pin": "359000", + "barcode_format": "QR", + "barcode_string": "7756580246805369", + "expiry_date": "2028-04-30", + "url": "https://spend.runa.io/-/cc3ec71d-6086-4bb7-a61a-55b42e098233", + "error_code": null, + "error_string": null + }, + "error_code": null, + "error_string": null, + "error_details": null +} +``` + +```json Error +{ + "status": "ERROR", + "order_id": "O-QRXNZBJV", + "error_code": "RE004", + "error_string": "The e-code has expired. You may need to place a new order.", + "error_details": "Asset has expired" +} +``` + + ## Error codes | Code | Description | From 09080b538395cb2a91c627ade5c646d8d89de148 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 18:39:08 +0200 Subject: [PATCH 14/17] Move JSON examples into ResponseExample side panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mintlify hoists ResponseExample (and RequestExample) into the side panel that lines up with the request — same pattern the original page used. Drop the in-body CodeGroup and add a sibling ResponseExample next to RequestExample at the bottom. Sanitise the success values to placeholders so they can't be confused for real codes/URLs. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 62 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index c013ba5..5ebb214 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -141,7 +141,32 @@ To see each response shape in practice, place a sync order for one of the produc | `ABO-GB` | GBP, face value `5` | `type: "code"`, with `code` + `pin` + `QR` barcode | | `LUSH-CA` | CAD, face value `10` | `type: "url"` | - +## Error codes + +| Code | Description | +| ------- | -------------------------------------------------------------------------------------------------------------- | +| `RE001` | The e-code has not been found. Please check the link. | +| `RE002` | This account has not been activated or is no longer active. Please contact Runa. | +| `RE003` | The e-code has been invalidated. Please contact Runa. | +| `RE004` | The e-code has expired. You may need to place a new order. | +| `RE005` | The e-code has been cancelled. | +| `RE006` | The e-code could not be redeemed at this time as there was no product stock available. Please try again later. | +| `RE007` | The e-code could not be redeemed at this time as the product is not active. Please contact Runa. | +| `RE008` | A processing error occurred. Please check the status page and try again later. | +| `RE009` | The request was placed too soon after a processing error. Please wait at least 1 minute before retrying. | +| `RE010` | Your account is out of funds. Please check your account balance before trying again. | +| `RE011` | The token was not found. Please check if your token is correct. | +| `RE012` | The order item is invalid. The order item may have been cancelled or refunded. | +| `RE013` | Feature not enabled. | +| `RE999` | An unexpected error occurred. Please contact Runa or try again later. | + + +```http Request +GET https://spend.runa.io/-/abc123…def?format=json +``` + + + ```json Success { "status": "SUCCESS", @@ -150,12 +175,12 @@ To see each response shape in practice, place a sync order for one of the produc "type": "code", "status": "SUCCESS", "amount": "5.00", - "code": "7756580246805369", - "pin": "359000", + "code": "1234567890123456", + "pin": "123456", "barcode_format": "QR", - "barcode_string": "7756580246805369", + "barcode_string": "1234567890123456", "expiry_date": "2028-04-30", - "url": "https://spend.runa.io/-/cc3ec71d-6086-4bb7-a61a-55b42e098233", + "url": "https://spend.runa.io/-/abc123…def", "error_code": null, "error_string": null }, @@ -174,29 +199,4 @@ To see each response shape in practice, place a sync order for one of the produc "error_details": "Asset has expired" } ``` - - -## Error codes - -| Code | Description | -| ------- | -------------------------------------------------------------------------------------------------------------- | -| `RE001` | The e-code has not been found. Please check the link. | -| `RE002` | This account has not been activated or is no longer active. Please contact Runa. | -| `RE003` | The e-code has been invalidated. Please contact Runa. | -| `RE004` | The e-code has expired. You may need to place a new order. | -| `RE005` | The e-code has been cancelled. | -| `RE006` | The e-code could not be redeemed at this time as there was no product stock available. Please try again later. | -| `RE007` | The e-code could not be redeemed at this time as the product is not active. Please contact Runa. | -| `RE008` | A processing error occurred. Please check the status page and try again later. | -| `RE009` | The request was placed too soon after a processing error. Please wait at least 1 minute before retrying. | -| `RE010` | Your account is out of funds. Please check your account balance before trying again. | -| `RE011` | The token was not found. Please check if your token is correct. | -| `RE012` | The order item is invalid. The order item may have been cancelled or refunded. | -| `RE013` | Feature not enabled. | -| `RE999` | An unexpected error occurred. Please contact Runa or try again later. | - - -```http Request -GET https://spend.runa.io/-/abc123…def?format=json -``` - + From e0c02cae6d61e492510c65893abae48c9da3d5e0 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 18:41:33 +0200 Subject: [PATCH 15/17] Note that errors come back as HTTP 200 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Redemption API returns HTTP 200 for both successful redemptions and documented RE* errors — integrators need to switch on the JSON status field rather than the HTTP status code. Add a Note at the top of the Response section calling this out. Verified against Playground: SUCCESS and RE006 both return 200. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 5ebb214..9f780a1 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -41,6 +41,12 @@ GET {payout.url}?format=json ## Response + + Both success and error responses return HTTP `200`. Check the JSON `status` + field (and `error_code` when it's `ERROR`) to distinguish them — don't rely + on the HTTP status code. + + `SUCCESS` or `ERROR`. On `ERROR`, see the top-level `error_code` and `error_string`. On `SUCCESS`, redemption details are in `e_code`. From 2e4ccd32b688d3952aa3aeed7356095f4c505a92 Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 18:52:14 +0200 Subject: [PATCH 16/17] Add render-whatever-is-non-null reminder under examples table Repeat the integration guidance from the e_code description right below the test-product table, so readers who jump straight to the examples don't miss it. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index 9f780a1..d1ea071 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -147,6 +147,11 @@ To see each response shape in practice, place a sync order for one of the produc | `ABO-GB` | GBP, face value `5` | `type: "code"`, with `code` + `pin` + `QR` barcode | | `LUSH-CA` | CAD, face value `10` | `type: "url"` | + + The brand decides which subset is populated; render whatever is available + rather than branching on the combination. + + ## Error codes | Code | Description | From 446adc05ce8ecbe6a863e38c667b653123f17cda Mon Sep 17 00:00:00 2001 From: Maike Fischer Date: Thu, 30 Apr 2026 19:05:26 +0200 Subject: [PATCH 17/17] Add code-only and code+barcode rows to examples table Full Playground catalog sweep (492 products) found test cases for the remaining reproducible shapes: BAW-GB returns code-only; CDISC-FR returns code + barcode without a PIN. Add rows for both, ordered from simplest to most complex shape. Pin-only and pin+barcode shapes don't appear in any Playground product and aren't included. Co-Authored-By: Claude Opus 4.7 (1M context) --- features/redemption-api.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/redemption-api.mdx b/features/redemption-api.mdx index d1ea071..534f6af 100644 --- a/features/redemption-api.mdx +++ b/features/redemption-api.mdx @@ -142,7 +142,9 @@ To see each response shape in practice, place a sync order for one of the produc | Product code | Order as | Returned shape | | ------------ | --------------------- | -------------------------------------------------------- | +| `BAW-GB` | GBP, face value `5` | `type: "code"`, with `code` only | | `FIVEG-US` | USD, face value `5` | `type: "code"`, with `code` + `pin` | +| `CDISC-FR` | EUR, face value `20` | `type: "code"`, with `code` + `code-128` barcode | | `AUCHAN-FR` | EUR, face value `10` | `type: "code"`, with `code` + `pin` + `code-128` barcode | | `ABO-GB` | GBP, face value `5` | `type: "code"`, with `code` + `pin` + `QR` barcode | | `LUSH-CA` | CAD, face value `10` | `type: "url"` |