{
  "openapi": "3.1.0",
  "info": {
    "title": "Vouchereto Merchant API",
    "version": "1.0.0",
    "summary": "Accept Vouchereto prepaid vouchers as a payment or top-up method.",
    "description": "Reserve and capture voucher redemptions, send customers to the hosted checkout or the hosted redeem widget,\ncreate test vouchers in the sandbox and list settlements.\n\n**Environments.** One host for both. The API key decides the mode: `vch_test_…` keys work on sandbox data,\n`vch_live_…` keys on real money (only once your merchant account is `live`). Data never crosses modes.\n\n**Authentication.** Every request carries `Authorization: Bearer <api key>` **and** a `Vouchereto-Signature`\nheader: `t=<unix seconds>,v1=<hex HMAC-SHA256>` computed with your key's secret (the last `_` segment of the key)\nover `t + \".\" + METHOD + \" \" + path_with_query + \"\\n\" + raw_body`. The path includes `/v1`. GET requests sign an\nempty body. Clock tolerance: 300 seconds. You may send several `v1=` values (useful while rotating keys).\n\n**Idempotency.** Every POST requires an `Idempotency-Key` header (1–128 chars). Repeating a request with the same\nkey and body returns the original response with `Idempotent-Replayed: true`; the same key with a different body\nreturns `409 IDEMPOTENCY_CONFLICT`.\n\n**Errors** follow RFC 7807 (`application/problem+json`). Always log `request_id`; support uses it to find your call.\n\nGuides, webhooks and test credentials: https://www.vouchereto.com/docs\n",
    "contact": {
      "name": "Vouchereto developer support",
      "url": "https://www.vouchereto.com/en/contact?topic=developer"
    }
  },
  "servers": [
    {
      "url": "https://api.vouchereto.com/v1",
      "description": "Production and sandbox (the key prefix selects the mode)"
    },
    {
      "url": "http://localhost:3003/v1",
      "description": "Local development stack (scripts/local/start-all.sh)"
    }
  ],
  "security": [
    {
      "apiKey": [],
      "signature": []
    }
  ],
  "tags": [
    {
      "name": "Redemptions",
      "description": "Two-step redemption of a voucher code your customer typed in your site (reserve, then capture or cancel)."
    },
    {
      "name": "Checkout",
      "description": "Hosted \"Pay with voucher\" checkout. The customer buys a voucher by card on pay.vouchereto.com and comes back."
    },
    {
      "name": "Redeem widget",
      "description": "Hosted page where the customer types an existing voucher code, so the code never touches your site."
    },
    {
      "name": "Sandbox",
      "description": "Test-mode helpers. Test keys only."
    },
    {
      "name": "Settlements",
      "description": "Weekly payouts of captured redemptions."
    }
  ],
  "paths": {
    "/redemptions": {
      "post": {
        "tags": [
          "Redemptions"
        ],
        "operationId": "createRedemption",
        "summary": "Reserve a voucher (step 1 of 2)",
        "description": "Locks the voucher's full value for `ttl_seconds`. Nothing is charged until you capture. If you neither capture nor\ncancel, the reservation expires and the voucher becomes usable again (`redemption.expired` webhook).\nVouchers are single-use: the whole face value is redeemed.\n\nInvalid codes are rate limited per `customer_ref`, per customer IP and per merchant (5 failures / 15 min per\ncustomer, then exponential blocks). Send `X-Customer-Ip` so one abusive customer cannot block your server's IP.\n",
        "x-rate-limit": "120 requests / minute / key",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Signature"
          },
          {
            "$ref": "#/components/parameters/CustomerIp"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedemptionCreate"
              },
              "example": {
                "code": "K7QM-4XPD-9TRW-2WNZ",
                "currency": "USD",
                "customer_ref": "user_8842",
                "ttl_seconds": 900
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reserved",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Redemption"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "409": {
            "$ref": "#/components/responses/Problem"
          },
          "422": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "Redemptions"
        ],
        "operationId": "listRedemptions",
        "summary": "List redemptions",
        "description": "Newest first. Cursor pagination.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Signature"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/RedemptionStatus"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`pagination.next_cursor` from the previous page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of redemptions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Redemption"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/redemptions/{id}": {
      "get": {
        "tags": [
          "Redemptions"
        ],
        "operationId": "getRedemption",
        "summary": "Retrieve a redemption",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "responses": {
          "200": {
            "description": "The redemption",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Redemption"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/redemptions/{id}/capture": {
      "post": {
        "tags": [
          "Redemptions"
        ],
        "operationId": "captureRedemption",
        "summary": "Capture a reservation (step 2 of 2)",
        "description": "Credits your merchant balance with the voucher's value. Credit your customer only after a 200 here (or the `redemption.captured` webhook).",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "responses": {
          "200": {
            "description": "Captured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Redemption"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "409": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/redemptions/{id}/cancel": {
      "post": {
        "tags": [
          "Redemptions"
        ],
        "operationId": "cancelRedemption",
        "summary": "Cancel a reservation",
        "description": "Releases the voucher so the customer can use it again. Only reservations can be cancelled; a captured redemption is final.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Redemption"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "409": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/checkout-sessions": {
      "post": {
        "tags": [
          "Checkout"
        ],
        "operationId": "createCheckoutSession",
        "summary": "Create a hosted checkout session",
        "description": "Redirect your customer to `url`. They buy a voucher by card (3D Secure) on pay.vouchereto.com. With\n`auto_redeem: true` (default) the voucher is reserved and captured for you in the same step and the session\nbecomes `completed` with a `redemption_id`. The customer is sent back to\n`return_url?vouchereto_session=<id>&status=<status>`. Trust the `checkout.completed` webhook or\n`GET /checkout-sessions/{id}`, never the query string alone. Sessions expire after 60 minutes.\n",
        "x-rate-limit": "300 requests / minute / key",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutSessionCreate"
              },
              "example": {
                "amount_minor": 5000,
                "currency": "USD",
                "customer_ref": "user_8842",
                "locale": "en",
                "return_url": "https://shop.example.com/topup/done",
                "cancel_url": "https://shop.example.com/topup",
                "metadata": {
                  "cart": "3141"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "422": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/checkout-sessions/{id}": {
      "get": {
        "tags": [
          "Checkout"
        ],
        "operationId": "getCheckoutSession",
        "summary": "Retrieve a checkout session",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "responses": {
          "200": {
            "description": "The session",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/redeem-sessions": {
      "post": {
        "tags": [
          "Redeem widget"
        ],
        "operationId": "createRedeemSession",
        "summary": "Create a hosted redeem session",
        "description": "Redirect your customer to `url`; they type their code on pay.vouchereto.com. They return to\n`return_url?vouchereto_redemption=<id>&status=<reserved|captured>`. With `auto_capture: false` (default) you must\ncapture or cancel the redemption yourself. Sessions expire after 30 minutes.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeemSessionCreate"
              },
              "example": {
                "currency": "USD",
                "customer_ref": "user_8842",
                "return_url": "https://shop.example.com/wallet",
                "auto_capture": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedeemSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "422": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/sandbox/vouchers": {
      "post": {
        "tags": [
          "Sandbox"
        ],
        "operationId": "createSandboxVoucher",
        "summary": "Create a test voucher",
        "description": "Test keys only (a live key gets `403 LIVEMODE_NOT_ALLOWED`). Returns a real, valid code in the state you ask for, tied to your merchant.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxVoucherCreate"
              },
              "example": {
                "state": "active",
                "amount_minor": 5000,
                "currency": "USD"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxVoucher"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "422": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/settlements": {
      "get": {
        "tags": [
          "Settlements"
        ],
        "operationId": "listSettlements",
        "summary": "List settlements",
        "description": "Latest 100, newest first. Test keys always get an empty list (no money moves in the sandbox). The line-level CSV is in the merchant portal.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Signature"
          }
        ],
        "responses": {
          "200": {
            "description": "Settlements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Settlement"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "redemption.captured": {
      "post": {
        "$ref": "#/components/pathItems/WebhookPost/post"
      }
    },
    "redemption.cancelled": {
      "post": {
        "$ref": "#/components/pathItems/WebhookPost/post"
      }
    },
    "redemption.expired": {
      "post": {
        "$ref": "#/components/pathItems/WebhookPost/post"
      }
    },
    "checkout.completed": {
      "post": {
        "$ref": "#/components/pathItems/WebhookPost/post"
      }
    },
    "checkout.expired": {
      "post": {
        "$ref": "#/components/pathItems/WebhookPost/post"
      }
    },
    "checkout.failed": {
      "post": {
        "$ref": "#/components/pathItems/WebhookPost/post"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "vch_(live|test)_<12 chars>_<32 chars>"
      },
      "signature": {
        "type": "apiKey",
        "in": "header",
        "name": "Vouchereto-Signature",
        "description": "t=<unix>,v1=<hex HMAC-SHA256(key secret, t + \".\" + METHOD + \" \" + path_with_query + \"\\n\" + raw_body)>"
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique per logical operation (for example your order id). Reuse it on retries.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      },
      "Signature": {
        "name": "Vouchereto-Signature",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "example": "t=1767225600,v1=5f2b…"
        }
      },
      "CustomerIp": {
        "name": "X-Customer-Ip",
        "in": "header",
        "required": false,
        "description": "Your end customer's IP. Used for brute-force protection instead of your server's IP.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "IdempotentReplayed": {
        "description": "Present (`true`) when this is a stored response to a repeated Idempotency-Key",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Problem": {
        "description": "Error (RFC 7807)",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "pathItems": {
      "WebhookPost": {
        "post": {
          "summary": "Event sent to your endpoint",
          "description": "Headers: `Vouchereto-Signature: t=<unix>,v1=<hex HMAC-SHA256(whsec secret, t + \".\" + raw_body)>`,\n`Vouchereto-Event-Id`, `User-Agent: Vouchereto-Webhooks/1.0`. Reply 2xx within 10 seconds. Retries after\n1 m, 5 m, 30 m, 2 h, 6 h, 12 h, 24 h, 24 h, then the delivery is marked dead (resend it from the portal).\n",
          "requestBody": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEvent"
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "Acknowledged"
            }
          }
        }
      }
    },
    "schemas": {
      "Currency": {
        "type": "string",
        "pattern": "^[A-Z]{3}$",
        "description": "ISO 4217 code accepted by your merchant account",
        "example": "USD"
      },
      "AmountMinor": {
        "type": "integer",
        "description": "Amount in minor units (cents). JPY and KRW have no decimals: 1000 = ¥1,000.",
        "example": 5000
      },
      "RedemptionStatus": {
        "type": "string",
        "enum": [
          "reserved",
          "captured",
          "cancelled",
          "expired"
        ]
      },
      "Pagination": {
        "type": "object",
        "required": [
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RedemptionCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code",
          "currency",
          "customer_ref"
        ],
        "properties": {
          "code": {
            "type": "string",
            "minLength": 16,
            "maxLength": 40,
            "description": "Code as typed by the customer; dashes and spaces are ignored"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "customer_ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Your id for the customer (no personal data)"
          },
          "ttl_seconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 1800,
            "default": 900
          }
        }
      },
      "Redemption": {
        "type": "object",
        "required": [
          "id",
          "object",
          "status",
          "amount_minor",
          "currency",
          "voucher_last4",
          "customer_ref",
          "livemode",
          "expires_at",
          "captured_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "redemption"
          },
          "status": {
            "$ref": "#/components/schemas/RedemptionStatus"
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "voucher_last4": {
            "type": "string",
            "example": "2WNZ"
          },
          "customer_ref": {
            "type": "string"
          },
          "livemode": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When an uncaptured reservation lapses"
          },
          "captured_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CheckoutSessionCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "amount_minor",
          "currency",
          "customer_ref",
          "return_url",
          "cancel_url"
        ],
        "properties": {
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "customer_ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "customer_email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Prefills the email field"
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Z]{2}$",
            "description": "Customer country (ISO 3166-1 alpha-2)"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "es",
              "ja",
              "ko",
              "pt",
              "it",
              "de"
            ],
            "default": "en"
          },
          "return_url": {
            "type": "string",
            "format": "uri",
            "description": "https:// (http://localhost allowed outside production)"
          },
          "cancel_url": {
            "type": "string",
            "format": "uri"
          },
          "auto_redeem": {
            "type": "boolean",
            "default": true,
            "description": "Apply the purchased voucher to your merchant in the same step"
          },
          "metadata": {
            "type": "object",
            "maxProperties": 20,
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            }
          }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "required": [
          "id",
          "object",
          "url",
          "status",
          "amount_minor",
          "currency",
          "customer_ref",
          "locale",
          "auto_redeem",
          "livemode",
          "redemption_id",
          "metadata",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "checkout_session"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://pay.vouchereto.com/c/0b7c…"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "paid",
              "completed",
              "expired",
              "cancelled",
              "failed"
            ]
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "customer_ref": {
            "type": "string"
          },
          "locale": {
            "type": "string"
          },
          "auto_redeem": {
            "type": "boolean"
          },
          "livemode": {
            "type": "boolean"
          },
          "redemption_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RedeemSessionCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "currency",
          "customer_ref",
          "return_url"
        ],
        "properties": {
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "customer_ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "return_url": {
            "type": "string",
            "format": "uri"
          },
          "auto_capture": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "RedeemSession": {
        "type": "object",
        "required": [
          "id",
          "object",
          "url",
          "status",
          "auto_capture",
          "livemode",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "redeem_session"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://pay.vouchereto.com/r/5d1e…"
          },
          "status": {
            "type": "string"
          },
          "auto_capture": {
            "type": "boolean"
          },
          "livemode": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SandboxVoucherCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "active",
              "reserved",
              "redeemed",
              "expired",
              "risk_hold",
              "void"
            ],
            "default": "active"
          },
          "amount_minor": {
            "type": "integer",
            "minimum": 100,
            "maximum": 10000000,
            "default": 5000
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          }
        }
      },
      "SandboxVoucher": {
        "type": "object",
        "required": [
          "id",
          "object",
          "code",
          "state",
          "amount_minor",
          "currency",
          "livemode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "sandbox_voucher"
          },
          "code": {
            "type": "string",
            "example": "K7QM-4XPD-9TRW-2WNZ"
          },
          "state": {
            "type": "string"
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "livemode": {
            "const": false
          }
        }
      },
      "Settlement": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "settlement"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "gross_minor": {
            "type": "integer"
          },
          "commission_minor": {
            "type": "integer"
          },
          "net_minor": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created_at",
          "livemode",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Same as the Vouchereto-Event-Id header; use it to deduplicate"
          },
          "type": {
            "type": "string",
            "enum": [
              "redemption.captured",
              "redemption.cancelled",
              "redemption.expired",
              "checkout.completed",
              "checkout.expired",
              "checkout.failed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "livemode": {
            "type": "boolean"
          },
          "data": {
            "oneOf": [
              {
                "title": "Redemption event data",
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "$ref": "#/components/schemas/RedemptionStatus"
                  },
                  "amount_minor": {
                    "type": "integer"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "customer_ref": {
                    "type": "string"
                  },
                  "voucher_last4": {
                    "type": "string"
                  },
                  "captured_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  }
                }
              },
              {
                "title": "Checkout event data",
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "type": "string"
                  },
                  "amount_minor": {
                    "type": "integer"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "customer_ref": {
                    "type": "string"
                  },
                  "redemption_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  },
                  "metadata": {
                    "type": "object"
                  }
                }
              }
            ]
          }
        }
      },
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "example": "https://www.vouchereto.com/docs/errors/voucher_invalid"
          },
          "title": {
            "type": "string",
            "example": "voucher invalid"
          },
          "status": {
            "type": "integer",
            "example": 404
          },
          "code": {
            "type": "string",
            "enum": [
              "UNAUTHENTICATED",
              "INVALID_SIGNATURE",
              "FORBIDDEN",
              "MERCHANT_NOT_ACTIVE",
              "LIVEMODE_NOT_ALLOWED",
              "NOT_FOUND",
              "VOUCHER_INVALID",
              "REDEMPTION_NOT_FOUND",
              "SESSION_NOT_FOUND",
              "VOUCHER_RESERVED",
              "VOUCHER_ALREADY_REDEEMED",
              "REDEMPTION_NOT_CAPTURABLE",
              "REDEMPTION_ALREADY_CAPTURED",
              "IDEMPOTENCY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "VOUCHER_EXPIRED",
              "CURRENCY_MISMATCH",
              "CURRENCY_NOT_ACCEPTED",
              "UNIVERSAL_NOT_ACCEPTED",
              "AMOUNT_OUT_OF_RANGE",
              "COUNTRY_NOT_ALLOWED",
              "PSP_PROGRAM_NOT_APPROVED",
              "STATE_INVALID",
              "VALIDATION_ERROR",
              "IDEMPOTENCY_KEY_REQUIRED",
              "RATE_LIMITED",
              "TOO_MANY_INVALID_ATTEMPTS"
            ]
          },
          "detail": {
            "type": "string",
            "description": "For INVALID_SIGNATURE: MALFORMED, TIMESTAMP_OUT_OF_RANGE or MISMATCH"
          },
          "request_id": {
            "type": "string"
          }
        }
      }
    }
  }
}
