{
  "openapi": "3.1.0",
  "info": {
    "title": "ripper API",
    "version": "2026-09-01",
    "summary": "Take payments with ripper checkout and follow them from your server.",
    "description": "The ripper API lets your server create checkout sessions, read payments, and manage the webhooks\nripper sends when a payment reaches an outcome.\n\n**Conventions.** Requests and responses are JSON. Field names are snake_case. Amounts are whole\nnumbers in the currency's minor unit (pence for GBP). Fields with no value are sent as `null`, never\nleft out. Times are ISO 8601 with an offset.\n\n**Test and live.** Every session is created in `test` or `live` mode, and the mode is shown on the\nsession and on each payment. A session is `live` only when your merchant account is live, your live\ntoggle is on, the integration is set to `auto`, and the key used is not a test-only key. Anything else\nis `test`. A live-only key that would land in test is refused with `key_live_only`.\n\n**Errors.** Checkout and payment routes answer every failure with\n`{\"error\": \"<API error code>\", \"message\": \"<text or null>\", \"request_id\": \"<id>\"}` and the same\nrequest id in the `Rip-Request-Id` header. Branch on `error`, never on `message`. The webhook\nroutes use the shapes described on each route.\n\n**Idempotency.** Send a `Rip-Idempotency-Key` header on every create. Retrying with the same key and\nthe same body returns the first response, marked `Rip-Idempotent-Replayed: true`; the same key with a\ndifferent body is refused.\n"
  },
  "x-ripper-drop-sha": "21afcc44516412a6dc9edf73fb63bf24ef017981",
  "servers": [
    {
      "url": "https://api.ripper.dev"
    }
  ],
  "tags": [
    {
      "name": "Checkout sessions",
      "description": "Create a session on your server and hand its client secret to the checkout on your page."
    },
    {
      "name": "Payments",
      "description": "Read the payment a checkout session produced."
    },
    {
      "name": "Webhook endpoints",
      "description": "Tell ripper where to send payment webhooks. Each endpoint gets its own signing secret."
    },
    {
      "name": "Webhook deliveries",
      "description": "See what ripper sent to your endpoints, and send a failed delivery again."
    },
    {
      "name": "Checkout (browser)",
      "description": "Routes the checkout calls from the shopper's browser for you. You do not call these yourself when you\nuse the script tag, the npm package or the React component; they are listed so you can see what\ncrosses the network.\n"
    },
    {
      "name": "Stripe compatibility",
      "description": "Stripe-shaped routes so an existing Stripe server integration can create and read payments on ripper.\nField names here keep Stripe's spelling.\n"
    }
  ],
  "paths": {
    "/v1/checkout/sessions": {
      "post": {
        "operationId": "createCheckoutSession",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "drop-checkout-session-create",
        "tags": [
          "Checkout sessions"
        ],
        "summary": "Create a checkout session",
        "description": "Creates a session for one amount. Send the `client_secret` from the response to your page and pass it\nto the checkout; never log it or store it. The session expires 30 minutes after it is created. A\nsession created on your server counts as a verified amount.\n\nRequires a secret key with the `create_payments` permission. The session uses your default\nintegration.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCheckoutSessionRequest"
              },
              "example": {
                "amount": 2500,
                "currency": "GBP",
                "customer": {
                  "email": "shopper@example.com",
                  "name": "Sam Shopper"
                },
                "metadata": {
                  "order_id": "1042"
                },
                "statement_descriptor_suffix": "ORDER 1042",
                "order_reference": "ORDER-1042"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session was created, or a retry with the same idempotency key replayed it.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              },
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateCheckoutSessionResponse"
                },
                "example": {
                  "id": "cs_0123456789abcdef01234567",
                  "client_secret": "cs_0123456789abcdef01234567_secret_9f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0",
                  "status": "requires_payment",
                  "mode": "test",
                  "amount": 2500,
                  "currency": "GBP",
                  "public_key": "rip_pk_EXAMPLE-KEY_00000000abcZ",
                  "config_url": "/v1/checkout/config/rip_pk_EXAMPLE-KEY_00000000abcZ",
                  "created_at": "2026-09-16T10:15:00.0000000+00:00",
                  "expires_at": "2026-09-16T10:45:00.0000000+00:00",
                  "expires_in_seconds": 1800
                }
              }
            }
          },
          "400": {
            "description": "`idempotency_key_missing`: no usable `Rip-Idempotency-Key` (missing, empty, longer than 255\ncharacters, or not printable ASCII).\n`invalid_request`: the body is not a JSON object; `amount` is not a positive whole number;\n`currency` is not three letters; `order_reference`, `customer.account_id` or `confirmation_url`\nbreaks its rule; a basket line breaks one of the rules on `items`; the basket's discounts come to\nmore than everything else on it; or the body carries a field this route does not accept. Since\nv1.13 an unrecognised field is refused rather than ignored, and the message names its JSON path —\n`items[2].tax_ammount` — so a typo tells you where it is instead of going quietly missing. The\nkeys inside a `metadata` object are yours and are never checked this way.\n`metadata_too_large`: more than 50 metadata entries, metadata over 8 KB, a `customer.metadata`\nover its own separate budget of the same size, or a `statement_descriptor_suffix` over 22\ncharacters.\n`card_data_rejected`: a value looks like a card number. The value is never echoed back.\n",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "An amount must be a positive number of MINOR units. Zero and negative amounts are refused here rather than sent to an acquirer that would answer differently for each of them.",
                  "request_id": "0123456789abcdef0123456789abcdef"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/SecretKeyUnauthorized"
          },
          "403": {
            "description": "`permission_denied`: the key does not have `create_payments`.\n`merchant_not_active`: your account cannot take payments right now.\n`key_live_only`: a live-only key would have created a test session.\n",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "permission_denied",
                  "message": null,
                  "request_id": "0123456789abcdef0123456789abcdef"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_key_reused`: the key was already used with a different body or on a different\nroute, or a request with this key is still running. Use a new key for a new session.\n",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "idempotency_key_reused",
                  "message": "This idempotency key was already used for a DIFFERENT request. Answering with the first response would hand back a session for an amount this caller did not ask for.",
                  "request_id": "0123456789abcdef0123456789abcdef"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "`store_unavailable`: ripper could not check the idempotency key. Retry with the same key.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "store_unavailable",
                  "message": "The idempotency store is unavailable, so this request cannot be told apart from a retry of one already served. Retry after 1 seconds.",
                  "request_id": "0123456789abcdef0123456789abcdef"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout/sessions/{session_id}": {
      "get": {
        "operationId": "getCheckoutSession",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "drop-checkout-session-get",
        "tags": [
          "Checkout sessions"
        ],
        "summary": "Retrieve a checkout session",
        "description": "Returns one of your sessions. A session past its expiry reads as `expired` straight away. The\nsession's `status` is the checkout's own progress; `succeeded` means the shopper paid, and the\npayment itself is read with `payment_id`.\n\nRequires a secret key with the `read_transactions` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                },
                "example": {
                  "id": "cs_0123456789abcdef01234567",
                  "status": "succeeded",
                  "mode": "test",
                  "amount": 2500,
                  "currency": "GBP",
                  "customer": {
                    "email": "shopper@example.com",
                    "name": "Sam Shopper",
                    "account_created": null,
                    "account_id": "your-customer-4821",
                    "metadata": {
                      "tier": "gold"
                    }
                  },
                  "metadata": {
                    "order_id": "1042"
                  },
                  "attempt_count": 1,
                  "last_decline": null,
                  "payment_id": "pay_89abcdef0123456789abcdef",
                  "settlement": null,
                  "created_at": "2026-09-16T10:15:00.0000000+00:00",
                  "expires_at": "2026-09-16T10:45:00.0000000+00:00",
                  "expires_in_seconds": 1612,
                  "integration": "Default",
                  "minted_by": "server",
                  "amount_verified": true,
                  "amount_verification_source": "server_minted",
                  "amount_verification_detail": null,
                  "hold": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/SecretKeyUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/SecretKeyForbiddenRead"
          },
          "404": {
            "$ref": "#/components/responses/ResourceNotFound"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/payments/{payment_id}": {
      "get": {
        "operationId": "getPayment",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "drop-payment-get",
        "tags": [
          "Payments"
        ],
        "summary": "Retrieve a payment",
        "description": "Returns one of your payments. `status` is `captured` when the money was taken. A payment on hold has\n`status: held` and `capture_state: authorised` until the hold is captured or voided.\n\nRequires a secret key with the `read_transactions` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PaymentId"
          }
        ],
        "responses": {
          "200": {
            "description": "The payment.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "id": "pay_89abcdef0123456789abcdef",
                  "checkout_session_id": "cs_0123456789abcdef01234567",
                  "status": "captured",
                  "amount": 2500,
                  "currency": "GBP",
                  "mode": "test",
                  "decline": null,
                  "authentication": {
                    "outcome": "Authenticated",
                    "liability_shifted": true,
                    "eci": null
                  },
                  "acquirer_transaction_id": null,
                  "created_at": "2026-09-16T10:17:41.0000000+00:00",
                  "executed_at": "2026-09-16T10:17:42.0000000+00:00",
                  "amount_verified": true,
                  "capture_state": "captured",
                  "customer": {
                    "email": "shopper@example.com",
                    "name": "Sam Shopper",
                    "account_created": null,
                    "account_id": "your-customer-4821",
                    "metadata": {
                      "tier": "gold"
                    }
                  },
                  "order_reference": "ORDER-1042",
                  "order_reference_captured_payments": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/SecretKeyUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/SecretKeyForbiddenRead"
          },
          "404": {
            "$ref": "#/components/responses/ResourceNotFound"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/webhooks/endpoints": {
      "post": {
        "operationId": "createWebhookEndpoint",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "manor-webhook-endpoints-create",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Create a webhook endpoint",
        "description": "Registers an https URL that receives payment webhooks. The response is the only time the\n`signing_secret` is shown: store it where your webhook handler can read it.\n\nLeave `event_filter` out to receive every payment event. A filter names the event types to receive,\nfrom the list this route accepts.\n\nRequires a secret key with the `manage_webhooks` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookEndpointRequest"
              },
              "example": {
                "url": "https://shop.example.com/webhooks/ripper"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The endpoint was created, or a retry with the same idempotency key replayed it.",
            "headers": {
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              },
              "Rip-Idempotency-Warning": {
                "$ref": "#/components/headers/RipIdempotencyWarning"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointCreated"
                },
                "example": {
                  "id": "whep_0123456789abcdef01234567",
                  "url": "https://shop.example.com/webhooks/ripper",
                  "event_filter": [],
                  "status": "active",
                  "signing_secret": "rip_whsec_EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXA",
                  "created_at": "2026-09-16T10:00:00.0000000+00:00"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_url`: the URL is missing, not absolute, not https, or points at a private or local\naddress. `unknown_event_type`: a filter entry is not an accepted event type; `allowed` lists\nthe accepted ones. `invalid_idempotency_key`: the idempotency key is unusable (for example, two\ndifferent keys on one request).\n",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/WebhookEndpointError"
                    },
                    {
                      "$ref": "#/components/schemas/GuardError"
                    }
                  ]
                },
                "example": {
                  "error": "invalid_url",
                  "message": "The webhook url must use https.",
                  "allowed": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ManorUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ManorForbidden"
          },
          "409": {
            "$ref": "#/components/responses/ManorIdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/ManorRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ManorInternalError"
          },
          "503": {
            "$ref": "#/components/responses/ManorIdempotencyStoreUnavailable"
          }
        }
      },
      "get": {
        "operationId": "listWebhookEndpoints",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "manor-webhook-endpoints-list",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "List webhook endpoints",
        "description": "Returns your active endpoints. With none registered the list is empty. Signing secrets are never\nreturned here.\n\nRequires a secret key with the `manage_webhooks` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Your active endpoints.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                },
                "example": {
                  "endpoints": [
                    {
                      "id": "whep_0123456789abcdef01234567",
                      "url": "https://shop.example.com/webhooks/ripper",
                      "event_filter": [],
                      "status": "active",
                      "created_at": "2026-09-16T10:00:00.0000000+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ManorUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ManorForbidden"
          },
          "429": {
            "$ref": "#/components/responses/ManorRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ManorInternalError"
          }
        }
      }
    },
    "/v1/webhooks/endpoints/{endpoint_id}": {
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "manor-webhook-endpoints-delete",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Stops deliveries to an endpoint. An unknown id, another account's id and an endpoint already deleted\nall answer the same 404.\n\nRequires a secret key with the `manage_webhooks` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EndpointId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "responses": {
          "204": {
            "description": "The endpoint was deleted. There is no body.",
            "headers": {
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              },
              "Rip-Idempotency-Warning": {
                "$ref": "#/components/headers/RipIdempotencyWarning"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ManorInvalidIdempotencyKey"
          },
          "401": {
            "$ref": "#/components/responses/ManorUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ManorForbidden"
          },
          "404": {
            "description": "`endpoint_not_found`: no active endpoint with this id on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointError"
                },
                "example": {
                  "error": "endpoint_not_found",
                  "message": "No such webhook endpoint.",
                  "allowed": null
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/ManorIdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/ManorRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ManorInternalError"
          },
          "503": {
            "$ref": "#/components/responses/ManorIdempotencyStoreUnavailable"
          }
        }
      }
    },
    "/v1/webhooks/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "manor-webhook-deliveries-list",
        "tags": [
          "Webhook deliveries"
        ],
        "summary": "List webhook deliveries",
        "description": "Returns delivery attempts to your endpoints, newest first, a page at a time. Pass `next_cursor` back\nas `after` for the next page, with the same filters. Each row is one attempt; a delivery that was\nretried has one row per attempt. Rows are kept for 30 days.\n\nRequires a secret key with the `manage_webhooks` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows per page, 1 to 100. Defaults to 20.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only rows with this delivery status.",
            "schema": {
              "$ref": "#/components/schemas/WebhookDeliveryStatus"
            }
          },
          {
            "name": "event_type",
            "in": "query",
            "required": false,
            "description": "Only rows for this delivery type, spelled as the row's `event_type` (for example `webhook.payment.captured`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of delivery rows.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryPage"
                },
                "example": {
                  "data": [
                    {
                      "delivery_id": "0123456789abcdef0123456789abcdef",
                      "replay_of": null,
                      "merchant_id": "m_your_merchant_id",
                      "event_id": "evt:pay_89abcdef0123456789abcdef#payment.captured#whep_0123456789abcdef01234567",
                      "event_type": "webhook.payment.captured",
                      "attempt_number": 1,
                      "status": "succeeded",
                      "occurred_at": "2026-09-16T10:17:43.0000000+00:00",
                      "endpoint_url": "https://shop.example.com/webhooks/ripper",
                      "response_status_code": 200,
                      "response_body": "ok",
                      "next_retry_at": null,
                      "replayable": false,
                      "replay_expires_at": "2026-10-16T10:17:42.0000000+00:00"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "`invalid_pagination`: `limit` is outside 1 to 100, `offset` was sent, or `after` is not a cursor\nfor these filters. `invalid_status`: `status` is not one of the delivery statuses.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuardError"
                },
                "example": {
                  "error": "invalid_pagination",
                  "message": "limit must be an integer in (0, 100]."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ManorUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ManorForbidden"
          },
          "429": {
            "$ref": "#/components/responses/ManorRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ManorInternalError"
          }
        }
      }
    },
    "/v1/webhooks/deliveries/{delivery_id}/replay": {
      "post": {
        "operationId": "replayWebhookDelivery",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "manor-webhook-delivery-replay",
        "tags": [
          "Webhook deliveries"
        ],
        "summary": "Replay a webhook delivery",
        "description": "Queues a failed delivery to be sent again. Only a delivery whose status is `dead_lettered` can be\nreplayed (its row shows `replayable: true`), within 30 days of the original delivery. The replay is\na new delivery with its own `delivery_id`. Send no body.\n\nReplays are limited to 10 a minute per account, whatever your plan.\n\nRequires a secret key with the `manage_webhooks` permission.\n",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DeliveryId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "responses": {
          "202": {
            "description": "The replay was queued.",
            "headers": {
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              },
              "Rip-Idempotency-Warning": {
                "$ref": "#/components/headers/RipIdempotencyWarning"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookReplayAccepted"
                },
                "example": {
                  "delivery_id": "89abcdef0123456789abcdef01234567",
                  "replay_of": "0123456789abcdef0123456789abcdef"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ManorInvalidIdempotencyKey"
          },
          "401": {
            "$ref": "#/components/responses/ManorUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ManorForbidden"
          },
          "404": {
            "description": "`delivery_not_found`: no webhook delivery with this id on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookReplayError"
                },
                "example": {
                  "error": "delivery_not_found"
                }
              }
            }
          },
          "409": {
            "description": "`delivery_not_dead_lettered`: the delivery has not been given up on yet, so it cannot be replayed.\n`replay_in_progress`: another replay of this delivery was accepted at the same moment.\n`idempotency_key_reused` or `idempotency_key_in_use`: see the idempotency key.\n",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/WebhookReplayError"
                    },
                    {
                      "$ref": "#/components/schemas/GuardError"
                    }
                  ]
                },
                "example": {
                  "error": "delivery_not_dead_lettered"
                }
              }
            }
          },
          "422": {
            "description": "`replay_window_expired`: the original delivery is more than 30 days old.\n`replay_chain_broken`: an earlier delivery in this replay chain is no longer stored.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookReplayError"
                },
                "example": {
                  "error": "replay_window_expired"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/ManorRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ManorInternalError"
          },
          "503": {
            "$ref": "#/components/responses/ManorIdempotencyStoreUnavailable"
          }
        }
      }
    },
    "/v1/checkout/config/{public_key}": {
      "get": {
        "operationId": "getCheckoutConfig",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-config-get",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Read the checkout configuration for a publishable key",
        "description": "What the checkout needs to draw itself for your integration. No credential beyond the publishable\nkey. Cached for 5 minutes and answered with an ETag. An unknown, revoked or unusable key, or an\nunknown integration name, all answer the same 404.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicKey"
          },
          {
            "name": "integration",
            "in": "query",
            "required": false,
            "description": "The integration's name. Without it the default integration is used.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The configuration.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/PublicCacheControl"
              },
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutConfig"
                }
              }
            }
          },
          "304": {
            "description": "The configuration has not changed since the ETag you sent.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/HandleNotFound"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ThreeDsProviderUnavailable"
          }
        }
      }
    },
    "/v1/checkout/methods/{public_key}": {
      "get": {
        "operationId": "getCheckoutPaymentMethods",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-methods-get",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Read the payment methods for a publishable key",
        "description": "Card is the only method today. Cached for 5 minutes. An unusable key answers 404.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The methods.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/PublicCacheControl"
              },
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutPaymentMethods"
                },
                "example": {
                  "methods": [
                    {
                      "type": "card",
                      "brands": [
                        "visa",
                        "mastercard",
                        "amex"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/HandleNotFound"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkout/handles/{handle}": {
      "get": {
        "operationId": "getHostedPageConfig",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-handle-get",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Read the hosted payment page configuration for a handle",
        "description": "Used by the hosted payment page at `pay.ripper.dev/{handle}`. Answers the same body as the\nconfiguration read, for your hosted payment page integration. The handle must match exactly.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The hosted payment page's configuration.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/PublicCacheControl"
              },
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutConfig"
                }
              }
            }
          },
          "304": {
            "description": "Not changed since the ETag you sent.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/HandleNotFound"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ThreeDsProviderUnavailable"
          }
        }
      }
    },
    "/v1/checkout/public/sessions": {
      "post": {
        "operationId": "createCheckoutSessionFromBrowser",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-public-session-create",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Create a checkout session from the browser",
        "description": "How the checkout creates a session when your page has no server-created one. Keyed by the\npublishable key in the body; the request's `Origin` must be one of the integration's allowed\norigins. The amount is verified against the integration's default or price list where it can be,\nand an integration that requires a verified amount refuses one that is not.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrowserCreateSessionRequest"
              },
              "example": {
                "public_key": "rip_pk_EXAMPLE-KEY_00000000abcZ",
                "amount": 2500,
                "currency": "GBP",
                "customer": {
                  "email": "shopper@example.com"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session was created, or replayed for the same idempotency key.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              },
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserCreateSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "`idempotency_key_missing`, `invalid_request`, `currency_required`, `metadata_too_large` or `card_data_rejected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` (the key is malformed, unknown or unusable) or `key_revoked`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`origin_not_allowed`, `merchant_not_active` or `key_live_only`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`resource_not_found`: no integration with the name sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`amount_unverified`: the integration requires a verified amount and this one could not be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "`store_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout/handles/{handle}/sessions": {
      "post": {
        "operationId": "createHostedPageSession",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-handle-session-create",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Create a session for the hosted payment page",
        "description": "The hosted payment page's create. The publishable key is found through the handle, and the hosted\npayment page integration is always used. An unknown handle answers `handle_not_found`.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrowserCreateSessionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session was created, or replayed for the same idempotency key.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              },
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserCreateSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "`idempotency_key_missing`, `invalid_request`, `currency_required`, `metadata_too_large` or `card_data_rejected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` or `key_revoked`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`origin_not_allowed`, `merchant_not_active` or `key_live_only`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`handle_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`amount_unverified`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "`store_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout/sessions/{session_id}/state": {
      "get": {
        "operationId": "getCheckoutSessionState",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-session-state-get",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Read the session's state with its client secret",
        "description": "How the checkout resumes a session in the browser, for example after a reload. A wrong secret and\nan unknown session answer the same 401. Never cached.\n",
        "security": [
          {
            "clientSecret": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The session's state.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSessionState"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ClientSecretUnauthorized"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkout/sessions/{session_id}/authenticate": {
      "post": {
        "operationId": "authenticateCheckoutSession",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-session-authenticate",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Start card authentication for a session",
        "description": "Opens a payment attempt with the card reference the checkout's card fields produced, and starts\n3-D Secure. A retry answers the same attempt. A session allows 3 attempts.\n",
        "security": [
          {
            "clientSecret": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthenticateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The attempt, and where to show the challenge.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthenticateResponse"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request` or `card_data_rejected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ClientSecretUnauthorized"
          },
          "409": {
            "description": "`session_expired`, `session_declined`, `session_completed`, `session_processing` or `attempts_exhausted`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`acquiring_profile_incomplete`: the account is not set up to take live card payments yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ThreeDsProviderUnavailable"
          }
        }
      }
    },
    "/v1/checkout/sessions/{session_id}/confirm": {
      "post": {
        "operationId": "confirmCheckoutSession",
        "x-ripper-audience": "browser",
        "x-ripper-route-id": "drop-checkout-session-confirm",
        "tags": [
          "Checkout (browser)"
        ],
        "summary": "Confirm the payment",
        "description": "Charges the authenticated attempt. A card decline is a 200 with `status: declined`, not an error.\nEvery payment needs the shopper's email: on the session, or as `customer.email` here. Retrying the\nsame attempt answers the recorded result.\n",
        "security": [
          {
            "clientSecret": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfirmRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The payment's outcome.",
            "headers": {
              "Rip-Request-Id": {
                "$ref": "#/components/headers/RipRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfirmResult"
                }
              }
            }
          },
          "400": {
            "description": "`customer_email_required` (no email on the session or in the body), `invalid_request` or `card_data_rejected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ClientSecretUnauthorized"
          },
          "409": {
            "description": "`session_expired`, or `authentication_incomplete` with the attempt and challenge to finish\n(`three_ds.state` is `awaiting_challenge` or `not_started`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/AuthenticationIncompleteError"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "`store_unavailable` or `three_ds_provider_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/compat/stripe/payment_intents": {
      "post": {
        "operationId": "createStripePaymentIntent",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "drop-compat-stripe-payment-intent-create",
        "tags": [
          "Stripe compatibility"
        ],
        "summary": "Create a PaymentIntent (Stripe-compatible)",
        "description": "Creates a ripper checkout session and answers it as a Stripe PaymentIntent; `id` is the session id.\nAccepts both forms an unchanged Stripe server sends: form encoding (what Stripe's libraries send,\nwith `metadata[key]=value`) and JSON. A parameter ripper does not support is refused by name with\n`parameter_unknown` rather than ignored. `description` is kept as `metadata.description`.\n\nUses the same secret key as the rest of the API, in Stripe's position: `Authorization: Bearer`.\n",
        "security": [
          {
            "stripeCompatBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. `Rip-Idempotency-Key` is accepted too. Without one the create still succeeds, but a retry creates a second session.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/StripeCreateBody"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StripeCreateBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PaymentIntent.",
            "headers": {
              "Ripper-Compat-Version": {
                "$ref": "#/components/headers/RipperCompatVersion"
              },
              "Rip-Idempotent-Replayed": {
                "$ref": "#/components/headers/RipIdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentIntent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/StripeError"
          },
          "401": {
            "$ref": "#/components/responses/StripeError"
          },
          "403": {
            "$ref": "#/components/responses/StripeError"
          },
          "409": {
            "$ref": "#/components/responses/StripeError"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/StripeError"
          },
          "503": {
            "$ref": "#/components/responses/StripeError"
          }
        }
      }
    },
    "/v1/compat/stripe/payment_intents/{session_id}": {
      "get": {
        "operationId": "getStripePaymentIntent",
        "x-ripper-audience": "back-end",
        "x-ripper-route-id": "drop-compat-stripe-payment-intent-get",
        "tags": [
          "Stripe compatibility"
        ],
        "summary": "Retrieve a PaymentIntent (Stripe-compatible)",
        "description": "Reads a session as a Stripe PaymentIntent. The client secret is not returned on a read.",
        "security": [
          {
            "stripeCompatBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The PaymentIntent.",
            "headers": {
              "Ripper-Compat-Version": {
                "$ref": "#/components/headers/RipperCompatVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentIntent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/StripeError"
          },
          "403": {
            "$ref": "#/components/responses/StripeError"
          },
          "404": {
            "$ref": "#/components/responses/StripeError"
          },
          "429": {
            "$ref": "#/components/responses/GatewayRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/StripeError"
          }
        }
      }
    }
  },
  "webhooks": {
    "payment.captured": {
      "post": {
        "operationId": "paymentCapturedWebhook",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "A payment was captured",
        "description": "Sent when money is taken: a payment captured straight away, or a held payment captured later. A\npayment captured after a hold carries `sequence: 2` and the decided `hold` block.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RipSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentWebhookEvent"
              },
              "example": {
                "id": "evt_fedcba9876543210fedcba98",
                "type": "payment.captured",
                "created": "2026-09-16T10:17:42.0000000+00:00",
                "api_version": "2026-09-01",
                "merchant_id": "m_your_merchant_id",
                "sequence": 1,
                "data": {
                  "payment_id": "pay_89abcdef0123456789abcdef",
                  "checkout_session_id": "cs_0123456789abcdef01234567",
                  "amount": 2500,
                  "currency": "GBP",
                  "status": "captured",
                  "mode": "test",
                  "decline": null,
                  "metadata": {
                    "order_id": "1042"
                  },
                  "amount_verified": true,
                  "capture_state": "captured",
                  "customer": {
                    "email": "shopper@example.com",
                    "name": "Sam Shopper",
                    "account_created": null,
                    "account_id": "your-customer-4821",
                    "metadata": {
                      "tier": "gold"
                    }
                  },
                  "hold": null,
                  "order_reference": "ORDER-1042",
                  "items": [
                    {
                      "kind": "product",
                      "sku": "SKU-1",
                      "name": "Merino scarf",
                      "quantity": 1,
                      "unit_amount": 2000,
                      "tax_amount": 333,
                      "tax_rate": 2000,
                      "type": "apparel"
                    },
                    {
                      "kind": "shipping",
                      "sku": null,
                      "name": "Next-day delivery",
                      "quantity": 1,
                      "unit_amount": 500,
                      "tax_amount": 83,
                      "tax_rate": 2000,
                      "type": null
                    }
                  ],
                  "basket_total": 2500,
                  "tax_total": 416
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAcknowledged"
          }
        }
      }
    },
    "payment.held": {
      "post": {
        "operationId": "paymentHeldWebhook",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "A payment is on hold",
        "description": "The card was authorised but not charged, because the amount could not be verified. The `hold` block\nsays why and by when it must be decided. A `payment.captured` or `payment.voided` follows.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RipSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentWebhookEvent"
              },
              "example": {
                "id": "evt_fedcba9876543210fedcba98",
                "type": "payment.held",
                "created": "2026-09-16T10:17:42.0000000+00:00",
                "api_version": "2026-09-01",
                "merchant_id": "m_your_merchant_id",
                "sequence": 1,
                "data": {
                  "payment_id": "pay_89abcdef0123456789abcdef",
                  "checkout_session_id": "cs_0123456789abcdef01234567",
                  "amount": 2500,
                  "currency": "GBP",
                  "status": "held",
                  "mode": "test",
                  "decline": null,
                  "metadata": {},
                  "amount_verified": false,
                  "capture_state": "authorised",
                  "customer": {
                    "email": "shopper@example.com",
                    "name": null,
                    "account_created": null
                  },
                  "hold": {
                    "status": "pending_review",
                    "reason": "no_price_list",
                    "detail": null,
                    "deadline": "2026-09-21T10:17:42.0000000+00:00",
                    "decided_by": null,
                    "decided_at": null,
                    "operation_refusal": null
                  },
                  "order_reference": null,
                  "items": null,
                  "basket_total": null,
                  "tax_total": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAcknowledged"
          }
        }
      }
    },
    "payment.voided": {
      "post": {
        "operationId": "paymentVoidedWebhook",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "A held payment was voided",
        "description": "The hold was released and the shopper was not charged. Carries `sequence: 2` and the decided `hold` block.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RipSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentWebhookEvent"
              },
              "example": {
                "id": "evt_ab1234567890abcdef123456",
                "type": "payment.voided",
                "created": "2026-09-16T10:19:03.0000000+00:00",
                "api_version": "2026-09-01",
                "merchant_id": "m_your_merchant_id",
                "sequence": 2,
                "data": {
                  "payment_id": "pay_89abcdef0123456789abcdef",
                  "checkout_session_id": "cs_0123456789abcdef01234567",
                  "amount": 2500,
                  "currency": "GBP",
                  "status": "voided",
                  "mode": "test",
                  "decline": null,
                  "metadata": {
                    "order_id": "1042"
                  },
                  "amount_verified": true,
                  "capture_state": "released",
                  "customer": {
                    "email": "shopper@example.com",
                    "name": "Sam Shopper",
                    "account_created": null,
                    "account_id": "your-customer-4821",
                    "metadata": {
                      "tier": "gold"
                    }
                  },
                  "hold": {
                    "status": "released",
                    "reason": "no_price_list",
                    "detail": null,
                    "deadline": "2026-09-21T10:17:42.0000000+00:00",
                    "decided_by": "merchant",
                    "decided_at": "2026-09-16T10:19:03.0000000+00:00",
                    "operation_refusal": null
                  },
                  "order_reference": "ORDER-1042",
                  "items": null,
                  "basket_total": null,
                  "tax_total": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAcknowledged"
          }
        }
      }
    },
    "payment.declined": {
      "post": {
        "operationId": "paymentDeclinedWebhook",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "A payment was declined",
        "description": "The card was declined. `decline` says why.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RipSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentWebhookEvent"
              },
              "example": {
                "id": "evt_cd1234567890abcdef123456",
                "type": "payment.declined",
                "created": "2026-09-16T10:19:03.0000000+00:00",
                "api_version": "2026-09-01",
                "merchant_id": "m_your_merchant_id",
                "sequence": 1,
                "data": {
                  "payment_id": "pay_89abcdef0123456789abcdef",
                  "checkout_session_id": "cs_0123456789abcdef01234567",
                  "amount": 2500,
                  "currency": "GBP",
                  "status": "declined",
                  "mode": "test",
                  "decline": {
                    "code": "card_declined",
                    "category": "hard",
                    "reason": "Do not honour"
                  },
                  "metadata": {
                    "order_id": "1042"
                  },
                  "amount_verified": true,
                  "capture_state": null,
                  "customer": {
                    "email": "shopper@example.com",
                    "name": "Sam Shopper",
                    "account_created": null,
                    "account_id": "your-customer-4821",
                    "metadata": {
                      "tier": "gold"
                    }
                  },
                  "hold": null,
                  "order_reference": "ORDER-1042",
                  "items": null,
                  "basket_total": null,
                  "tax_total": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAcknowledged"
          }
        }
      }
    },
    "payment.failed": {
      "post": {
        "operationId": "paymentFailedWebhook",
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "A payment failed",
        "description": "The payment could not be completed for a reason that is not a card decline.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RipSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentWebhookEvent"
              },
              "example": {
                "id": "evt_ef1234567890abcdef123456",
                "type": "payment.failed",
                "created": "2026-09-16T10:19:03.0000000+00:00",
                "api_version": "2026-09-01",
                "merchant_id": "m_your_merchant_id",
                "sequence": 1,
                "data": {
                  "payment_id": "pay_89abcdef0123456789abcdef",
                  "checkout_session_id": "cs_0123456789abcdef01234567",
                  "amount": 2500,
                  "currency": "GBP",
                  "status": "failed",
                  "mode": "test",
                  "decline": {
                    "code": "provider_error",
                    "category": "provider_error",
                    "reason": "The acquirer did not answer in time"
                  },
                  "metadata": {
                    "order_id": "1042"
                  },
                  "amount_verified": true,
                  "capture_state": null,
                  "customer": {
                    "email": "shopper@example.com",
                    "name": "Sam Shopper",
                    "account_created": null,
                    "account_id": "your-customer-4821",
                    "metadata": {
                      "tier": "gold"
                    }
                  },
                  "hold": null,
                  "order_reference": "ORDER-1042",
                  "items": null,
                  "basket_total": null,
                  "tax_total": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAcknowledged"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "secretKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Ripper-Api-Key",
        "description": "Your secret key, which starts `rip_sk_`. It is for your server only: never put it in a web page, an\napp or a repository. Read it from an environment variable.\n"
      },
      "clientSecret": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Ripper-Client-Secret",
        "description": "The session's client secret, which the checkout sends from the shopper's browser. It works for that one session only."
      },
      "stripeCompatBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your ripper secret key — the same `rip_sk_` key as the rest of the API — sent as `Authorization: Bearer` where Stripe's libraries put a Stripe key."
      }
    },
    "parameters": {
      "IdempotencyKeyRequired": {
        "name": "Rip-Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A key you choose for this create, for example your order id plus an attempt number. Retrying with\nthe same key and body returns the first response instead of creating a second session. Up to 255\nprintable ASCII characters.\n",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        },
        "example": "order-1042"
      },
      "IdempotencyKeyOptional": {
        "name": "Rip-Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional but recommended. A retry with the same key returns the first response. Without a key the\nresponse carries `Rip-Idempotency-Warning`. `Idempotency-Key` is accepted as well; sending two\ndifferent keys is refused.\n",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "SessionId": {
        "name": "session_id",
        "in": "path",
        "required": true,
        "description": "The session's id.",
        "schema": {
          "type": "string",
          "pattern": "^cs_[0-9a-f]{24}$"
        },
        "example": "cs_0123456789abcdef01234567"
      },
      "PaymentId": {
        "name": "payment_id",
        "in": "path",
        "required": true,
        "description": "The payment's id.",
        "schema": {
          "type": "string",
          "pattern": "^pay_[0-9a-f]{24}$"
        },
        "example": "pay_89abcdef0123456789abcdef"
      },
      "EndpointId": {
        "name": "endpoint_id",
        "in": "path",
        "required": true,
        "description": "The endpoint's id.",
        "schema": {
          "type": "string",
          "pattern": "^whep_[0-9a-f]{24}$"
        },
        "example": "whep_0123456789abcdef01234567"
      },
      "DeliveryId": {
        "name": "delivery_id",
        "in": "path",
        "required": true,
        "description": "The `delivery_id` from a delivery row.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-f]{32}$"
        },
        "example": "0123456789abcdef0123456789abcdef"
      },
      "PublicKey": {
        "name": "public_key",
        "in": "path",
        "required": true,
        "description": "Your publishable key, which starts `rip_pk_`. It is safe to put in a web page; only the secret key is not.",
        "schema": {
          "type": "string",
          "pattern": "^rip_pk_[A-Za-z0-9_-]{24}$",
          "minLength": 31,
          "maxLength": 31
        },
        "example": "rip_pk_EXAMPLE-KEY_00000000abcZ"
      },
      "Handle": {
        "name": "handle",
        "in": "path",
        "required": true,
        "description": "Your hosted payment page handle, exactly as registered.",
        "schema": {
          "type": "string"
        }
      },
      "IfNoneMatch": {
        "name": "If-None-Match",
        "in": "header",
        "required": false,
        "description": "An ETag from an earlier read. A match answers 304.",
        "schema": {
          "type": "string"
        }
      },
      "Origin": {
        "name": "Origin",
        "in": "header",
        "required": true,
        "description": "The page's origin, which the browser sends. It must be one of the integration's allowed origins.",
        "schema": {
          "type": "string"
        }
      },
      "RipSignature": {
        "name": "Rip-Signature",
        "in": "header",
        "required": true,
        "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256>`. To verify: take the raw request body exactly as received,\ncompute HMAC-SHA256 over `<t>.<raw body>` with the endpoint's signing secret (the whole string,\nprefix included, as UTF-8), hex-encode it in lower case, and compare it with `v1` in constant time.\nReject a `t` that is too far from your clock (five minutes is a sensible tolerance) so a captured\nrequest cannot be replayed later.\n",
        "schema": {
          "type": "string",
          "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
        },
        "example": "t=1789553862,v1=5f2b8c0e9d4a7163b2e8f0c91d6a4b3e7c5f0a2d8b1e6c9f4a3d7b0e2c5f8a1d"
      },
      "WebhookIdempotencyKey": {
        "name": "Rip-Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "The same value for every attempt and replay of one event to one endpoint, and the row's `event_id`\nin the delivery list. Use it to ignore an event you have already handled.\n",
        "schema": {
          "type": "string",
          "maxLength": 128
        }
      }
    },
    "headers": {
      "RipRequestId": {
        "description": "The id of this request. Quote it if you contact support.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-f]{32}$"
        }
      },
      "RipIdempotentReplayed": {
        "description": "`true` when this is the stored response for an idempotency key already used.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      },
      "RipIdempotencyWarning": {
        "description": "Sent when a create or delete had no idempotency key.",
        "schema": {
          "type": "string"
        }
      },
      "XRateLimitLimit": {
        "description": "Requests allowed in the current minute for this kind of request on your account.",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitRemaining": {
        "description": "Requests left in the current minute.",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitReset": {
        "description": "When the minute resets, as a Unix timestamp in seconds.",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "ETag": {
        "description": "A weak entity tag for the body.",
        "schema": {
          "type": "string"
        }
      },
      "PublicCacheControl": {
        "description": "`public, max-age=300`.",
        "schema": {
          "type": "string"
        }
      },
      "RipperCompatVersion": {
        "description": "`stripe/1`.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "SecretKeyUnauthorized": {
        "description": "`unauthorized`: no key, or a key that is unknown, expired, or used from an address or origin the key\ndoes not allow. `key_revoked`: the key was revoked. `public_key_missing`: the key has no publishable\nkey paired with it.\n",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "unauthorized",
              "message": null,
              "request_id": "0123456789abcdef0123456789abcdef"
            }
          }
        }
      },
      "SecretKeyForbiddenRead": {
        "description": "`permission_denied`: the key does not have `read_transactions`. `merchant_not_active`: your account is not active.",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "permission_denied",
              "message": null,
              "request_id": "0123456789abcdef0123456789abcdef"
            }
          }
        }
      },
      "ResourceNotFound": {
        "description": "`resource_not_found`: there is no such id on your account. Another account's id answers the same.",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "resource_not_found",
              "message": "No such session for this merchant.",
              "request_id": "0123456789abcdef0123456789abcdef"
            }
          }
        }
      },
      "ClientSecretUnauthorized": {
        "description": "`unauthorized`: the client secret is missing or wrong, or the session does not exist.",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "`internal_error`: something failed on ripper's side. The body carries no message. Quote `request_id` to support.",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "internal_error",
              "message": null,
              "request_id": "0123456789abcdef0123456789abcdef"
            }
          }
        }
      },
      "ThreeDsProviderUnavailable": {
        "description": "`three_ds_provider_unavailable`: card authentication is unavailable for a moment. Try again.",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "HandleNotFound": {
        "description": "Not found. Since v1.13 this is the ordinary error body — `request_id` in the body as well as the\nheader, and `Cache-Control: no-store` — rather than the bare `{\"error\": \"handle_not_found\"}` it used\nto be. The bare body carried neither, which left a caching layer free to hold on to a 404 that a\nmerchant was in the middle of fixing.\n",
        "headers": {
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          },
          "Cache-Control": {
            "schema": {
              "type": "string",
              "enum": [
                "no-store"
              ]
            },
            "description": "This answer is never stored, so a fix takes effect on the next request."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "handle_not_found",
              "message": "No integration is published at that handle.",
              "request_id": "4f3c2b1a9e8d7c6b5a4f3e2d1c0b9a87"
            }
          }
        }
      },
      "GatewayRateLimited": {
        "description": "Too many requests from your address in a minute. This answer comes from ripper's gateway, so its\nbody is not the error shape above. Wait for `Retry-After` seconds.\n",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      },
      "ManorUnauthorized": {
        "description": "`unknown_principal`: no key, or a key that is not recognised. The body is `{\"code\": …}`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AuthError"
            },
            "example": {
              "code": "unknown_principal"
            }
          }
        }
      },
      "ManorForbidden": {
        "description": "The key was recognised but refused: `key_permission_denied` (no `manage_webhooks`), `key_revoked`,\n`key_expired`, `key_ip_blocked` or `key_origin_blocked`. The body is `{\"code\": …}`.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AuthError"
            },
            "example": {
              "code": "key_permission_denied"
            }
          }
        }
      },
      "ManorInvalidIdempotencyKey": {
        "description": "`invalid_idempotency_key`: the idempotency key is unusable, for example two different keys on one request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GuardError"
            }
          }
        }
      },
      "ManorIdempotencyConflict": {
        "description": "`idempotency_key_reused`: the key was used with a different request. `idempotency_key_in_use`: a request with this key is still running.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GuardError"
            },
            "example": {
              "error": "idempotency_key_reused",
              "message": "This idempotency key was already used with different parameters."
            }
          }
        }
      },
      "ManorIdempotencyStoreUnavailable": {
        "description": "`idempotency_store_unavailable`: the key could not be checked. Retry with the same key after `Retry-After` seconds.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GuardError"
            }
          }
        }
      },
      "ManorRateLimited": {
        "description": "`rate_limit_exceeded`: too many requests of this kind for your account this minute. By default reads\nallow 500 a minute, creates and deletes 100, and replays 10.\n",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/XRateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/XRateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/XRateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      },
      "ManorInternalError": {
        "description": "`internal_error`: something failed on ripper's side.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GuardError"
            },
            "example": {
              "error": "internal_error",
              "message": "The request could not be completed."
            }
          }
        }
      },
      "StripeError": {
        "description": "A Stripe-shaped error. `error.type` follows Stripe (`invalid_request_error`, `authentication_error`,\n`permission_error`, `api_error`); `error.code` is Stripe's code where one applies (`parameter_unknown`,\n`resource_missing`, `idempotency_key_in_use`) and otherwise the API error code, which is always in\n`error.ripper_code`.\n",
        "headers": {
          "Ripper-Compat-Version": {
            "$ref": "#/components/headers/RipperCompatVersion"
          },
          "Rip-Request-Id": {
            "$ref": "#/components/headers/RipRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/StripeErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "invalid_request_error",
                "code": "parameter_unknown",
                "message": "Received unknown parameter: payment_method_types[]. The ripper compatibility layer emulates PaymentIntent create and retrieve only.",
                "param": "payment_method_types[]",
                "ripper_code": "invalid_request"
              }
            }
          }
        }
      },
      "WebhookAcknowledged": {
        "description": "Answer any 2xx within 15 seconds to acknowledge. Anything else, or no answer, is retried after 1\nminute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours. A delivery still failing 72 hours after\nit was created is marked `dead_lettered` and can be replayed for 30 days.\n"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The error body of the checkout, payment and browser routes.",
        "required": [
          "error",
          "message",
          "request_id"
        ],
        "properties": {
          "error": {
            "description": "The error code. Branch on this, never on `message`.",
            "$ref": "#/components/schemas/ErrorCode"
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Help for a person reading logs. It may change; never branch on it."
          },
          "request_id": {
            "type": "string",
            "description": "The same value as the `Rip-Request-Id` header."
          },
          "retry_after_seconds": {
            "type": "integer",
            "description": "How long to wait before sending the same request again. Present ONLY on `store_unavailable`, and\nomitted — not `null` — on every other refusal, so no other error body changes shape. The same\nnumber is on the `Retry-After` header.\n\nNOT the same field as the `retry_after_seconds` on a `processing` confirm response, which is how\nlong a checkout in a browser waits before asking again. That one is a polling interval of a couple\nof seconds on a request that has not failed; this one is a back-off on a request that has. One\nname, two surfaces, two very different numbers: use the one on the body you are actually holding.\n"
          }
        }
      },
      "ErrorCode": {
        "type": "string",
        "description": "Every API error code the checkout and payment routes can return (38).",
        "enum": [
          "acquiring_profile_incomplete",
          "amount_unverified",
          "attempts_exhausted",
          "authentication_incomplete",
          "capture_window_ended",
          "card_data_rejected",
          "compat_provider_unknown",
          "currency_required",
          "customer_email_required",
          "handle_not_found",
          "hold_already_decided",
          "hold_duration_out_of_range",
          "idempotency_key_missing",
          "idempotency_key_reused",
          "integration_builtin",
          "integration_name_taken",
          "internal_error",
          "invalid_request",
          "invalid_url",
          "key_live_only",
          "key_revoked",
          "merchant_not_active",
          "metadata_too_large",
          "operation_in_progress",
          "origin_invalid",
          "origin_not_allowed",
          "permission_denied",
          "public_key_missing",
          "resource_not_found",
          "routing_unavailable",
          "session_completed",
          "session_declined",
          "session_expired",
          "session_processing",
          "store_unavailable",
          "three_ds_provider_unavailable",
          "unauthorized",
          "unknown_event_type"
        ]
      },
      "AuthError": {
        "type": "object",
        "description": "The webhook routes' answer when the key is refused.",
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "description": "Why the key was refused. Branch on this.",
            "type": "string",
            "enum": [
              "unknown_principal",
              "key_revoked",
              "key_expired",
              "key_ip_blocked",
              "key_origin_blocked",
              "key_permission_denied"
            ]
          }
        }
      },
      "GuardError": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "`invalid_idempotency_key`, `idempotency_key_reused`, `idempotency_key_in_use`, `idempotency_store_unavailable`, `internal_error`, `invalid_pagination` or `invalid_status`."
          },
          "message": {
            "description": "Help for a person reading logs. It may change; never branch on it.",
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RateLimitError": {
        "type": "object",
        "required": [
          "error",
          "message",
          "tier",
          "upgrade_url"
        ],
        "properties": {
          "error": {
            "description": "The error code.",
            "type": "string",
            "enum": [
              "rate_limit_exceeded"
            ]
          },
          "message": {
            "description": "Help for a person reading logs. It may change; never branch on it.",
            "type": "string"
          },
          "tier": {
            "description": "The rate-limit tier this account is on.",
            "type": "string"
          },
          "upgrade_url": {
            "description": "Where to read about a higher limit.",
            "type": "string"
          }
        }
      },
      "WebhookEndpointError": {
        "type": "object",
        "required": [
          "error",
          "message",
          "allowed"
        ],
        "properties": {
          "error": {
            "description": "The error code. Branch on this, never on `message`.",
            "type": "string",
            "enum": [
              "invalid_url",
              "unknown_event_type",
              "endpoint_not_found"
            ]
          },
          "message": {
            "description": "Help for a person reading logs. It may change; never branch on it.",
            "type": "string"
          },
          "allowed": {
            "type": [
              "array",
              "null"
            ],
            "description": "On `unknown_event_type`, the event types a filter may name. Otherwise null.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WebhookReplayError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "description": "The error code. Branch on this, never on `message`.",
            "type": "string",
            "enum": [
              "delivery_not_found",
              "delivery_not_dead_lettered",
              "replay_in_progress",
              "replay_window_expired",
              "replay_chain_broken"
            ]
          }
        }
      },
      "Customer": {
        "type": "object",
        "description": "The shopper, as you or the checkout supplied them.",
        "required": [
          "email",
          "name",
          "account_created",
          "account_id",
          "metadata"
        ],
        "properties": {
          "email": {
            "description": "The shopper’s email address, or `null` when none was given.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "The shopper’s name, or `null` when none was given.",
            "type": [
              "string",
              "null"
            ]
          },
          "account_created": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "When the shopper's account with you was created, if a browser session sent it."
          },
          "account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own identifier for this shopper, exactly as you sent it. `null` when none was sent. Set from\na browser session it is whatever the page sent, so treat it as the page's claim rather than as\nsomething ripper has checked.\n"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "description": "Your own values about this shopper, keys exactly as you sent them. `null` when none were sent.\nThis is the CUSTOMER's metadata; the session's own `metadata` is a separate field with a separate\nbudget, and the two are never merged.\n",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "Decline": {
        "type": "object",
        "required": [
          "code",
          "category",
          "reason"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "The decline code."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "How to treat the decline: `soft`, `hard`, `fraud`, `network` or `provider_error`. Since v1.13 the\nword is the same snake_case everywhere — these routes, the webhook and the checkout — where the\nmerchant routes and webhooks previously spelled it `Soft`, `Hard`, `ProviderError` and so on. It\nis the payment platform's own category, relayed unchanged rather than re-classified here.\n"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "The acquirer's own words, passed on as received."
          }
        }
      },
      "HoldBlock": {
        "type": "object",
        "description": "Why a payment is on hold and what was decided.",
        "required": [
          "status",
          "reason",
          "detail",
          "deadline",
          "decided_by",
          "decided_at",
          "operation_refusal"
        ],
        "properties": {
          "status": {
            "description": "Where the hold is. `pending_review` and `escalated` are both still waiting on a decision —\nescalated means a person is looking at it rather than a rule. `captured` means the money was\ntaken. `voided` means the hold was released and nothing was taken, and it is final: it is not\nanother kind of waiting.\n\nDO NOT RELEASE GOODS until this says `captured`. That holds for all four values, including\nescalated, which can still end either way.\n",
            "type": "string",
            "enum": [
              "pending_review",
              "escalated",
              "captured",
              "voided"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "no_price_list",
              "mismatch"
            ],
            "description": "`no_price_list`: there was nothing to check the amount against. `mismatch`: the amount did not match."
          },
          "detail": {
            "description": "More about why, when there is more to say.",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "amount_mismatch",
              "unknown_item",
              "currency_mismatch",
              "overflow",
              null
            ]
          },
          "deadline": {
            "type": "string",
            "format": "date-time",
            "description": "When the hold must be decided by."
          },
          "decided_by": {
            "description": "Who decided: you, or ripper on your rules.",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "policy",
              "agent",
              "merchant",
              "sweep",
              null
            ]
          },
          "decided_at": {
            "description": "When the decision was made, or `null` while it is still waiting.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "operation_refusal": {
            "type": [
              "object",
              "null"
            ],
            "description": "The last capture or void the acquirer refused, if any.",
            "required": [
              "kind",
              "sequence",
              "reason",
              "decline_code",
              "at"
            ],
            "properties": {
              "kind": {
                "description": "Which operation was refused.",
                "type": "string",
                "enum": [
                  "capture",
                  "void"
                ]
              },
              "sequence": {
                "description": "Which attempt this refusal belongs to.",
                "type": "integer"
              },
              "reason": {
                "description": "Why it was refused.",
                "type": "string",
                "enum": [
                  "capture_refused",
                  "void_refused"
                ]
              },
              "decline_code": {
                "description": "The decline code, when the refusal came from the acquirer.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "at": {
                "description": "When the refusal happened.",
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "SessionStatus": {
        "type": "string",
        "enum": [
          "requires_payment",
          "requires_authentication",
          "processing",
          "succeeded",
          "declined",
          "expired"
        ],
        "description": "`succeeded` means the shopper paid (the payment is `captured` or `held`)."
      },
      "AmountVerificationSource": {
        "type": "string",
        "enum": [
          "server_minted",
          "integration_default",
          "price_list",
          "none",
          "mismatch"
        ],
        "description": "`server_minted` for a session your server created."
      },
      "AmountVerificationDetail": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "amount_mismatch",
          "unknown_item",
          "currency_mismatch",
          "overflow",
          null
        ]
      },
      "CreateCheckoutSessionRequest": {
        "type": "object",
        "required": [
          "amount",
          "currency"
        ],
        "properties": {
          "amount": {
            "type": "integer",
            "minimum": 1,
            "description": "The amount in the currency's minor unit."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "A three-letter ISO 4217 code, for example `GBP`."
          },
          "items": {
            "type": "array",
            "maxItems": 100,
            "description": "The basket, shown to the shopper on the payment page and returned on every read of this session\nand its payment. At most 100 lines.\n\nTWO RULES THAT CATCH PEOPLE OUT, both of which refuse the whole request:\n\nA line that carries its own `unit_amount` must also carry a `name`. A `sku` alone is enough for a\nline you are pricing from your catalogue, but not for one you are pricing yourself — the name is\nwhat the shopper reads on the page, and ripper will not invent it.\n\nThe basket may not come to less than nothing. Discount lines are subtracted, so a basket whose\ndiscounts exceed everything else on it is refused rather than clamped to zero. This is the only\nrule here about the basket as a whole; every other rule is about one line.\n\n`amount` still decides what is charged. When the lines do not add up to it, the difference is\nshown to the shopper as one adjustment rather than the basket being refused.\n",
            "items": {
              "$ref": "#/components/schemas/BasketLineRequest"
            }
          },
          "customer": {
            "type": "object",
            "description": "The shopper. Every payment needs an email; if you leave it out here, the checkout asks the shopper for it.",
            "properties": {
              "email": {
                "description": "The shopper’s email address.",
                "type": "string"
              },
              "name": {
                "description": "The shopper’s name.",
                "type": "string"
              },
              "account_id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64,
                "description": "Your own identifier for this shopper, returned on the session read, the payment read and the\nwebhook. Same rule as `order_reference`: 1 to 64 characters, no control characters, and a run\nof 13 to 19 digits that could be a card number is refused. It is yours and is not checked for\nuniqueness.\n"
              },
              "metadata": {
                "type": "object",
                "description": "Up to 50 string values of your own about this shopper, 8 KB in total. This is `customer.metadata`\nand it has its OWN budget, separate from the session's top-level `metadata` — the two are\nmeasured apart, not against one shared limit. Keys come back exactly as you sent them.\n",
                "maxProperties": 50,
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          },
          "metadata": {
            "type": "object",
            "description": "Up to 50 string values of your own, 8 KB in total. Keys come back exactly as you sent them.",
            "maxProperties": 50,
            "additionalProperties": {
              "type": "string"
            }
          },
          "statement_descriptor_suffix": {
            "type": "string",
            "maxLength": 22,
            "description": "Added to the name on the shopper's card statement."
          },
          "order_reference": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Your order reference, shown to the shopper on the confirmation page and stored on the payment.\n1 to 64 characters with no control characters. A run of 13 to 19 digits that could be a card\nnumber is refused; use fewer digits or put a letter among them.\n"
          },
          "confirmation_url": {
            "type": "string",
            "maxLength": 512,
            "format": "uri",
            "description": "Where to send the shopper after the payment, instead of your integration's setting. An absolute\nhttps URL on one of your default integration's allowed origins, with no user information, no\nfragment, no IP address as the host, and no `payment_id` or `order_reference` query parameter\n(ripper adds those).\n"
          }
        }
      },
      "CreateCheckoutSessionResponse": {
        "type": "object",
        "required": [
          "id",
          "client_secret",
          "status",
          "mode",
          "amount",
          "currency",
          "public_key",
          "config_url",
          "created_at",
          "expires_at",
          "expires_in_seconds"
        ],
        "properties": {
          "id": {
            "description": "The session. Send its `client_secret` to your page; send this to us.",
            "type": "string",
            "pattern": "^cs_[0-9a-f]{24}$"
          },
          "client_secret": {
            "type": "string",
            "description": "Pass this to the checkout on your page. It is returned only here."
          },
          "status": {
            "description": "Where the session is in its life. A new session has taken no attempts yet.",
            "type": "string",
            "enum": [
              "requires_payment"
            ]
          },
          "mode": {
            "description": "`test` or `live`. A test session can take only test cards.",
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "amount": {
            "description": "What the shopper will be charged, in the currency’s minor unit.",
            "type": "integer"
          },
          "currency": {
            "description": "A three-letter ISO 4217 code, for example `GBP`.",
            "type": "string"
          },
          "public_key": {
            "type": "string",
            "description": "Your publishable key, which the checkout loads the session with."
          },
          "config_url": {
            "type": "string",
            "description": "The configuration read's path, relative to the API host."
          },
          "created_at": {
            "description": "When the session was created.",
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "description": "When the session stops accepting attempts.",
            "type": "string",
            "format": "date-time"
          },
          "expires_in_seconds": {
            "description": "How long the session has from now.",
            "type": "integer"
          }
        }
      },
      "BasketLineRequest": {
        "type": "object",
        "description": "One line of the basket you send. A refusal names the exact line and member it objected to, for\nexample `items[2].tax_amount`, so you do not have to guess which one.\n",
        "required": [
          "quantity"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "product",
              "shipping",
              "discount",
              "fee"
            ],
            "description": "What this line is. Absent means `product`. Delivery is a `shipping` line and a reduction is a\n`discount` line carrying a POSITIVE `unit_amount` that is subtracted — never a negative amount.\n"
          },
          "sku": {
            "type": "string",
            "maxLength": 64,
            "description": "Your code for the item. A line needs this or a `name`."
          },
          "name": {
            "type": "string",
            "maxLength": 200,
            "description": "What the shopper sees. A line needs this or a `sku`, and always needs it when the line carries its own `unit_amount`."
          },
          "quantity": {
            "description": "How many. At least 1.",
            "type": "integer",
            "minimum": 1
          },
          "unit_amount": {
            "type": "integer",
            "minimum": 0,
            "description": "What the shopper pays per unit, tax INCLUSIVE, in the currency's minor unit. Not negative: a\nreduction is a line whose `kind` is `discount`.\n"
          },
          "tax_amount": {
            "type": "integer",
            "minimum": 0,
            "description": "The tax INSIDE this line's total — that is, inside `quantity` x `unit_amount` — in minor units.\nBetween 0 and that total.\n"
          },
          "tax_rate": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "description": "This line's tax rate in basis points, where 2000 is 20.00%."
          },
          "type": {
            "type": "string",
            "maxLength": 32,
            "description": "Your own label for the kind of thing this is, at most 32 characters."
          }
        }
      },
      "BasketLine": {
        "type": "object",
        "description": "One line of the basket as ripper returns it. Every member is written, `null` where it is not set,\nso a reader never has to tell an absent member from an unset one.\n",
        "required": [
          "kind",
          "sku",
          "name",
          "quantity",
          "unit_amount",
          "tax_amount",
          "tax_rate",
          "type"
        ],
        "properties": {
          "kind": {
            "description": "What this line is: `product`, `shipping`, `discount` or `fee`. A discount carries a positive `unit_amount` that is subtracted.",
            "type": "string",
            "enum": [
              "product",
              "shipping",
              "discount",
              "fee"
            ]
          },
          "sku": {
            "description": "Your code for the item, or `null` if the line had none.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "What the shopper was shown, or `null` if the line was priced from your catalogue and carried no name.",
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "description": "How many.",
            "type": "integer"
          },
          "unit_amount": {
            "description": "What the shopper paid per unit, tax inclusive, in the currency’s minor unit. `null` on a line your catalogue has not priced.",
            "type": [
              "integer",
              "null"
            ]
          },
          "tax_amount": {
            "description": "The tax inside this line’s total, in minor units, or `null` when the line carries no tax.",
            "type": [
              "integer",
              "null"
            ]
          },
          "tax_rate": {
            "description": "This line’s tax rate in basis points, where 2000 is 20.00%, or `null` when none was set.",
            "type": [
              "integer",
              "null"
            ]
          },
          "type": {
            "description": "Your own label for the kind of thing this is, or `null`.",
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "required": [
          "id",
          "status",
          "mode",
          "amount",
          "currency",
          "customer",
          "metadata",
          "attempt_count",
          "last_decline",
          "payment_id",
          "settlement",
          "created_at",
          "expires_at",
          "expires_in_seconds",
          "integration",
          "minted_by",
          "amount_verified",
          "amount_verification_source",
          "amount_verification_detail",
          "hold"
        ],
        "properties": {
          "id": {
            "description": "The session. Send its `client_secret` to your page; send this to us.",
            "type": "string"
          },
          "status": {
            "description": "Where the session is in its life, from created to a finished payment or an expiry.",
            "$ref": "#/components/schemas/SessionStatus"
          },
          "mode": {
            "description": "`test` or `live`. A test session can take only test cards.",
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "amount": {
            "description": "What the shopper will be charged, in the currency’s minor unit.",
            "type": "integer"
          },
          "currency": {
            "description": "A three-letter ISO 4217 code, for example `GBP`.",
            "type": "string"
          },
          "items": {
            "type": [
              "array",
              "null"
            ],
            "description": "The basket, as sent on the server create. `null` when the session carried no lines — never an\nempty array, so \"no basket\" and \"an empty basket\" stay different answers.\n",
            "items": {
              "$ref": "#/components/schemas/BasketLine"
            }
          },
          "basket_total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What the lines add up to, in minor units, tax inclusive: products, shipping and fees added,\ndiscounts subtracted. Computed by ripper and never accepted in a request. `null` when there are\nno lines. When it differs from `amount`, `amount` is what was charged and the difference is shown\nto the shopper as one adjustment.\n"
          },
          "tax_total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The tax across the lines, in minor units. `null` when no line carries a `tax_amount`."
          },
          "customer": {
            "description": "The shopper, as you or the checkout supplied them.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Customer"
              },
              {
                "type": "null"
              }
            ]
          },
          "metadata": {
            "description": "The metadata you sent when you created this session, keys exactly as you sent them.",
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "attempt_count": {
            "type": "integer",
            "description": "Payment attempts opened, at most 3."
          },
          "last_decline": {
            "description": "The most recent decline on this session, or `null` when there has not been one.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Decline"
              },
              {
                "type": "null"
              }
            ]
          },
          "payment_id": {
            "description": "The payment this session produced, or `null` while it has not produced one.",
            "type": [
              "string",
              "null"
            ]
          },
          "settlement": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "awaiting_reconciliation",
              null
            ],
            "description": "`awaiting_reconciliation` while ripper confirms the outcome of an attempt the acquirer did not answer. Money may already have moved, so do not release goods and do not treat the payment as failed while it is set — reconciliation resolves it, not a clock in the checkout."
          },
          "created_at": {
            "description": "When the session was created.",
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "description": "When the session stops accepting attempts.",
            "type": "string",
            "format": "date-time"
          },
          "expires_in_seconds": {
            "type": "integer",
            "description": "Seconds left; 0 once expired."
          },
          "integration": {
            "type": [
              "string",
              "null"
            ],
            "description": "The name of the integration the session belongs to."
          },
          "minted_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "server",
              "client",
              null
            ],
            "description": "`server` when your server created the session, `client` when the checkout did."
          },
          "amount_verified": {
            "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
            "type": "boolean"
          },
          "amount_verification_source": {
            "description": "What ripper checked the amount against: the server that created it, your integration’s default, your price list, nothing, or a mismatch.",
            "$ref": "#/components/schemas/AmountVerificationSource"
          },
          "amount_verification_detail": {
            "description": "Why the amount did not verify, when the source is a mismatch. `null` otherwise.",
            "$ref": "#/components/schemas/AmountVerificationDetail"
          },
          "hold": {
            "description": "Why this session’s payment is on hold and what was decided, or `null` when it was never held.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/HoldBlock"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Payment": {
        "type": "object",
        "required": [
          "id",
          "checkout_session_id",
          "status",
          "amount",
          "currency",
          "mode",
          "decline",
          "authentication",
          "acquirer_transaction_id",
          "created_at",
          "executed_at",
          "amount_verified",
          "capture_state",
          "customer",
          "order_reference",
          "order_reference_captured_payments"
        ],
        "properties": {
          "id": {
            "description": "The payment. Quote this to us about anything that happened to the money.",
            "type": "string"
          },
          "checkout_session_id": {
            "description": "The session this payment came from.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "captured",
              "declined",
              "failed",
              "pending",
              "held",
              "voided"
            ],
            "description": "`captured`: paid. `held`: authorised and waiting on the hold decision. `voided`: a hold released. `pending`: the outcome is not known yet."
          },
          "amount": {
            "description": "What was charged, in the currency’s minor unit.",
            "type": "integer"
          },
          "currency": {
            "description": "A three-letter ISO 4217 code, for example `GBP`.",
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "description": "`test` or `live`. A test payment moved no money.",
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "decline": {
            "description": "Why the payment was declined, or `null` when it was not.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Decline"
              },
              {
                "type": "null"
              }
            ]
          },
          "authentication": {
            "type": [
              "object",
              "null"
            ],
            "description": "The 3-D Secure result, PRESENT EXACTLY WHEN AN AUTHENTICATION RAN and absent otherwise.\n\nThat absence carries a fact nothing else on the payment does. `liability_shifted` is false on a payment\nthat never authenticated AND on one that authenticated without shifting liability — two different\nsituations, and only the presence of this block tells them apart. If you are deciding who carries a\ndisputed payment, check whether the block is here before you read anything inside it.\n",
            "required": [
              "outcome",
              "liability_shifted",
              "eci"
            ],
            "properties": {
              "outcome": {
                "type": "string",
                "description": "How the 3-D Secure authentication went. Today the value arrives as `Authenticated`, `Attempted` or\n`Failed`. There is no fourth word: when the authentication produced anything else, this whole block is\nabsent rather than carrying another value.\n\nIt is what decides `liability_shifted` beside it, so it is the field to read when you want to know who\ncarries a disputed payment.\n\nCOMPARE IT IGNORING CASE AND SEPARATORS — fold the value to one case and remove any `_` or `-` before\nyou match it. Those capitals are what the API sends today and are not a convention: everything else\nhere is lower case with underscores, and whether this field should be too is under review. Ignoring\ncase alone would be enough for these three words and would quietly stop being enough the day a\ntwo-word value arrived as `two_words`; ignoring separators as well costs you nothing and is right\nwhichever way that decision goes.\n"
              },
              "liability_shifted": {
                "description": "Whether the card scheme moved liability for a disputed payment to the issuer. This is the point of 3-D Secure.",
                "type": "boolean"
              },
              "eci": {
                "description": "The scheme’s own code for how the authentication went. Your acquirer may ask for it.",
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "acquirer_transaction_id": {
            "description": "The bank’s own reference for this payment. Quote it to your acquirer, not to us.",
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "description": "When the payment was created.",
            "type": "string",
            "format": "date-time"
          },
          "executed_at": {
            "description": "When the money actually moved, or `null` if it never did.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "amount_verified": {
            "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
            "type": "boolean"
          },
          "capture_state": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "captured",
              "authorised",
              "voided",
              null
            ],
            "description": "Where the money is. `captured`: taken. `authorised`: the shopper's bank is holding it for you and\nit has not been taken yet. `voided`: the authorisation was released and nothing was taken.\n`null` when nothing was authorised at all — a decline or a failure.\n"
          },
          "items": {
            "type": [
              "array",
              "null"
            ],
            "description": "The basket, as sent on the server create. `null` when the session carried no lines — never an\nempty array, so \"no basket\" and \"an empty basket\" stay different answers.\n",
            "items": {
              "$ref": "#/components/schemas/BasketLine"
            }
          },
          "basket_total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What the lines add up to, in minor units, tax inclusive: products, shipping and fees added,\ndiscounts subtracted. Computed by ripper and never accepted in a request. `null` when there are\nno lines. When it differs from `amount`, `amount` is what was charged and the difference is shown\nto the shopper as one adjustment.\n"
          },
          "tax_total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The tax across the lines, in minor units. `null` when no line carries a `tax_amount`."
          },
          "customer": {
            "description": "The shopper, as you or the checkout supplied them.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Customer"
              },
              {
                "type": "null"
              }
            ]
          },
          "order_reference": {
            "description": "Your own reference for this order, or `null` when none was sent.",
            "type": [
              "string",
              "null"
            ]
          },
          "order_reference_captured_payments": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many captured payments share this order reference, THIS ONE INCLUDED. So an ordinary captured\npayment on its own reference reads 1, not 0.\n\n`null` — no order reference, so there is nothing to count.\n`0` — the reference exists and no money has been taken on it yet. This is the answer on a held\npayment, and it is the one to check before releasing goods.\n`1` — the ordinary case: this payment, and no other capture on the reference.\n`2` or more — almost always the shopper paid twice. Duplicates are surfaced, never refused, so\nthis is how you find out.\n\nCounts only your own payments: another merchant's payments on the same reference are not yours\nand are not counted.\n"
          }
        }
      },
      "CreateWebhookEndpointRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "An absolute https URL that is publicly reachable."
          },
          "event_filter": {
            "type": "array",
            "description": "The event types to receive, from the five under `webhooks`. Leave it out, or send an empty list, to\nreceive every payment event.\n",
            "items": {
              "type": "string",
              "enum": [
                "payment.captured",
                "payment.held",
                "payment.voided",
                "payment.declined",
                "payment.failed"
              ]
            }
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "url",
          "event_filter",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "description": "The endpoint. Use it to remove the endpoint or to list its deliveries.",
            "type": "string",
            "pattern": "^whep_[0-9a-f]{24}$"
          },
          "url": {
            "description": "Where deliveries are sent.",
            "type": "string"
          },
          "event_filter": {
            "description": "The event types this endpoint receives. Empty means every payment event.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "description": "Whether this endpoint is still receiving deliveries. Removing an endpoint disables it rather than deleting it.",
            "type": "string",
            "enum": [
              "active"
            ]
          },
          "created_at": {
            "description": "When the endpoint was registered.",
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "required": [
              "signing_secret"
            ],
            "properties": {
              "signing_secret": {
                "type": "string",
                "description": "The secret that signs this endpoint's webhooks. Shown only in this response."
              }
            }
          }
        ]
      },
      "WebhookEndpointList": {
        "type": "object",
        "required": [
          "endpoints"
        ],
        "properties": {
          "endpoints": {
            "description": "Your endpoints. An empty array when you have none — never a 404.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          }
        }
      },
      "WebhookDeliveryStatus": {
        "type": "string",
        "enum": [
          "pending",
          "succeeded",
          "failed",
          "dead_lettered"
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "required": [
          "delivery_id",
          "replay_of",
          "merchant_id",
          "event_id",
          "event_type",
          "attempt_number",
          "status",
          "occurred_at",
          "endpoint_url",
          "response_status_code",
          "response_body",
          "next_retry_at",
          "replayable",
          "replay_expires_at"
        ],
        "properties": {
          "delivery_id": {
            "type": "string",
            "description": "The delivery this attempt belongs to. Replay by this id."
          },
          "replay_of": {
            "type": [
              "string",
              "null"
            ],
            "description": "The delivery this one replays."
          },
          "merchant_id": {
            "description": "Your merchant account.",
            "type": "string"
          },
          "event_id": {
            "type": "string",
            "description": "The value sent in the delivery's `Rip-Idempotency-Key` header."
          },
          "event_type": {
            "type": "string",
            "description": "`webhook.` followed by the event type, for example `webhook.payment.captured`."
          },
          "attempt_number": {
            "type": "integer",
            "description": "1 for the first attempt. 0 marks a replay that was accepted and not yet attempted."
          },
          "status": {
            "description": "How this attempt ended.",
            "$ref": "#/components/schemas/WebhookDeliveryStatus"
          },
          "occurred_at": {
            "description": "When this attempt was made.",
            "type": "string",
            "format": "date-time"
          },
          "endpoint_url": {
            "description": "Where it was sent.",
            "type": [
              "string",
              "null"
            ]
          },
          "response_status_code": {
            "description": "What your endpoint answered, or `null` if it never answered.",
            "type": [
              "integer",
              "null"
            ]
          },
          "response_body": {
            "type": [
              "string",
              "null"
            ],
            "description": "The first 1,024 characters of your endpoint's answer."
          },
          "next_retry_at": {
            "description": "When ripper will try again, or `null` when it will not.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "replayable": {
            "description": "Whether you can ask for this delivery to be sent again.",
            "type": "boolean"
          },
          "replay_expires_at": {
            "description": "After this, the delivery can no longer be replayed.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WebhookDeliveryPage": {
        "type": "object",
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "description": "This page of deliveries, newest first.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            }
          },
          "has_more": {
            "description": "Whether there are older deliveries beyond this page.",
            "type": "boolean"
          },
          "next_cursor": {
            "description": "Pass this as `cursor` to get the next page, or `null` when this is the last.",
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WebhookReplayAccepted": {
        "type": "object",
        "required": [
          "delivery_id",
          "replay_of"
        ],
        "properties": {
          "delivery_id": {
            "type": "string",
            "description": "The new delivery."
          },
          "replay_of": {
            "type": "string",
            "description": "The delivery you replayed."
          }
        }
      },
      "PaymentWebhookEvent": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created",
          "api_version",
          "merchant_id",
          "sequence",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^evt_[0-9a-f]{24}$",
            "description": "The event's id. The same event to the same endpoint always has the same id."
          },
          "type": {
            "description": "Which event this is. The same five names your endpoint filter uses.",
            "type": "string",
            "enum": [
              "payment.captured",
              "payment.held",
              "payment.voided",
              "payment.declined",
              "payment.failed"
            ]
          },
          "created": {
            "description": "When ripper made this event, not when it was delivered. A retry of the same event keeps the original time.",
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "description": "The shape of this body. It changes only when the shape does, so you can branch on it if you keep old handlers running.",
            "type": "string",
            "enum": [
              "2026-09-01"
            ]
          },
          "merchant_id": {
            "type": "string",
            "description": "Your merchant account id (`m_` followed by 24 hexadecimal characters)."
          },
          "sequence": {
            "type": "integer",
            "description": "1 for a payment's outcome; 2 for the later decision on a held payment."
          },
          "data": {
            "description": "The payment this event is about.",
            "type": "object",
            "required": [
              "payment_id",
              "checkout_session_id",
              "amount",
              "currency",
              "status",
              "mode",
              "decline",
              "metadata",
              "amount_verified",
              "capture_state",
              "customer",
              "hold",
              "order_reference",
              "items",
              "basket_total",
              "tax_total"
            ],
            "properties": {
              "payment_id": {
                "description": "The payment. Use it to read the payment back, and as the key to make your handler idempotent.",
                "type": "string"
              },
              "checkout_session_id": {
                "description": "The session this payment came from.",
                "type": "string"
              },
              "amount": {
                "description": "What was charged, in the currency’s minor unit.",
                "type": "integer"
              },
              "currency": {
                "description": "A three-letter ISO 4217 code, for example `GBP`.",
                "type": "string"
              },
              "status": {
                "description": "The payment’s status at the moment this event was made.",
                "type": "string",
                "enum": [
                  "captured",
                  "held",
                  "voided",
                  "declined",
                  "failed"
                ]
              },
              "mode": {
                "description": "`test` or `live`. A test payment moved no money.",
                "type": "string",
                "enum": [
                  "test",
                  "live"
                ]
              },
              "decline": {
                "description": "Why the payment was declined, or `null` when it was not.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Decline"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "metadata": {
                "description": "The metadata you sent when you created the session, keys exactly as you sent them.",
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "amount_verified": {
                "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
                "type": "boolean"
              },
              "capture_state": {
                "description": "Where the money is: captured, authorised and waiting, or voided.",
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "captured",
                  "authorised",
                  "voided",
                  null
                ]
              },
              "items": {
                "type": [
                  "array",
                  "null"
                ],
                "description": "The basket, or `null` when the session carried no lines.",
                "items": {
                  "$ref": "#/components/schemas/BasketLine"
                }
              },
              "basket_total": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "What the lines add up to, in minor units, tax inclusive. `null` when there are no lines."
              },
              "tax_total": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The tax across the lines, in minor units. `null` when no line carries a `tax_amount`."
              },
              "customer": {
                "description": "The shopper, as you or the checkout supplied them.",
                "$ref": "#/components/schemas/Customer"
              },
              "hold": {
                "description": "Why this payment is on hold and what was decided, or `null` when it was never held.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/HoldBlock"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "order_reference": {
                "description": "Your own reference for this order, or `null` when none was sent.",
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "RequestedItem": {
        "type": "object",
        "required": [
          "quantity"
        ],
        "properties": {
          "sku": {
            "description": "Your code for the item. A line needs this or a `name`.",
            "type": "string",
            "maxLength": 64
          },
          "name": {
            "description": "What the shopper sees. A line needs this or a `sku`.",
            "type": "string",
            "maxLength": 200
          },
          "quantity": {
            "description": "How many.",
            "type": "integer",
            "minimum": 1
          },
          "type": {
            "description": "Your own label for the kind of thing this is.",
            "type": "string",
            "pattern": "^[A-Za-z0-9_.-]{1,32}$"
          }
        }
      },
      "BrowserCreateSessionRequest": {
        "type": "object",
        "properties": {
          "public_key": {
            "type": "string",
            "description": "Your publishable key. Not used on the hosted payment page route, which finds it through the handle."
          },
          "integration": {
            "type": "string",
            "description": "The integration's name. Without it, the default integration."
          },
          "amount": {
            "type": "integer",
            "description": "The amount, unless items or the integration's default give one."
          },
          "currency": {
            "type": "string",
            "description": "Three letters. Without it, the integration's or account's currency."
          },
          "items": {
            "type": "array",
            "maxItems": 100,
            "description": "What the shopper is buying. Each needs a positive quantity and a sku or a name.",
            "items": {
              "$ref": "#/components/schemas/RequestedItem"
            }
          },
          "customer": {
            "description": "The shopper. Every payment needs an email; if you leave it out, the checkout asks for it.",
            "type": "object",
            "properties": {
              "email": {
                "description": "The shopper’s email address.",
                "type": "string"
              },
              "name": {
                "description": "The shopper’s name.",
                "type": "string",
                "maxLength": 256
              },
              "account_created": {
                "description": "When the shopper’s account with you was created. Card schemes treat a long-standing account as lower risk.",
                "type": "string",
                "format": "date"
              }
            }
          },
          "metadata": {
            "description": "Up to 50 string values of your own, 8 KB in total. Keys come back exactly as you sent them.",
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "order_reference": {
            "description": "Your order reference, shown to the shopper on the confirmation page and stored on the payment.",
            "type": "string",
            "maxLength": 64
          },
          "confirmation_url": {
            "description": "Where to send the shopper after the payment, instead of your integration’s setting.",
            "type": "string",
            "maxLength": 512
          }
        }
      },
      "BrowserCreateSessionResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateCheckoutSessionResponse"
          },
          {
            "type": "object",
            "required": [
              "integration",
              "amount_verified",
              "amount_verification_source",
              "amount_verification_detail",
              "minted_by"
            ],
            "properties": {
              "integration": {
                "description": "The name of the integration this session was created on: the one you named, or your default integration when you named none. On the hosted payment page it is always that page’s own integration.",
                "type": "string"
              },
              "amount_verified": {
                "description": "Whether ripper could check the amount against this integration’s default amount or its price list. When it could not, an approved payment is held for you to review rather than captured. An integration set to require a verified amount refuses the request instead, and no session is created.",
                "type": "boolean"
              },
              "amount_verification_source": {
                "description": "What the amount was checked against: the integration’s default amount, its price list, nothing at all, or a disagreement with what the integration has configured. The `server_minted` value this field carries on a server-created session never appears here.",
                "type": "string",
                "enum": [
                  "integration_default",
                  "price_list",
                  "none",
                  "mismatch"
                ]
              },
              "amount_verification_detail": {
                "description": "Why the amount did not verify, when the source is `mismatch`. `null` in every other case, and always sent.",
                "$ref": "#/components/schemas/AmountVerificationDetail"
              },
              "minted_by": {
                "description": "Always `client` here: this session was created from the page, not from your server.",
                "type": "string",
                "enum": [
                  "client"
                ]
              }
            }
          }
        ]
      },
      "CheckoutSessionState": {
        "type": "object",
        "required": [
          "id",
          "status",
          "expires_in_seconds",
          "attempt_count",
          "last_decline",
          "payment_id",
          "payment_status",
          "amount_verified",
          "amount_verification_source",
          "amount_verification_detail",
          "hold",
          "mount",
          "integration",
          "minted_by",
          "amount",
          "currency",
          "billing_address_present",
          "customer_email_present",
          "country",
          "order_reference",
          "confirmation_url"
        ],
        "properties": {
          "id": {
            "description": "The session this state belongs to.",
            "type": "string"
          },
          "status": {
            "description": "Where the session is now. Read it after a reload to decide what to show.",
            "$ref": "#/components/schemas/SessionStatus"
          },
          "expires_in_seconds": {
            "description": "How long the session has left.",
            "type": "integer"
          },
          "attempt_count": {
            "description": "How many payment attempts this session has had.",
            "type": "integer"
          },
          "last_decline": {
            "description": "The most recent decline on this session, or `null` when there has not been one.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Decline"
              },
              {
                "type": "null"
              }
            ]
          },
          "payment_id": {
            "description": "The payment this session produced, or `null` while it has not produced one.",
            "type": [
              "string",
              "null"
            ]
          },
          "payment_status": {
            "description": "The payment’s status, or `null` while there is no payment.",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "captured",
              "declined",
              "pending",
              "held",
              "voided",
              null
            ]
          },
          "amount_verified": {
            "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
            "type": "boolean"
          },
          "amount_verification_source": {
            "description": "What the amount was checked against: the server that created it, the integration’s default, the price list, nothing, or a mismatch.",
            "$ref": "#/components/schemas/AmountVerificationSource"
          },
          "amount_verification_detail": {
            "description": "Why the amount did not verify, when the source is a mismatch. `null` otherwise.",
            "$ref": "#/components/schemas/AmountVerificationDetail"
          },
          "hold": {
            "description": "The hold on this session’s payment, or `null` when there is none.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/HoldBlock"
              },
              {
                "type": "null"
              }
            ]
          },
          "mount": {
            "type": [
              "object",
              "null"
            ],
            "description": "The open challenge to show, if any."
          },
          "integration": {
            "description": "Which of your integrations this session belongs to.",
            "type": [
              "string",
              "null"
            ]
          },
          "minted_by": {
            "description": "Whether this session was created from your server or from the page. A page-created amount is only as good as what the integration could check it against: `amount_verified` says whether it was, and a payment it could not verify is held for you to review rather than captured. A session your server created is never held for this reason.",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "server",
              "client",
              null
            ]
          },
          "amount": {
            "description": "What the shopper will be charged, in the currency’s minor unit.",
            "type": "integer"
          },
          "currency": {
            "description": "A three-letter ISO 4217 code, for example `GBP`.",
            "type": "string"
          },
          "billing_address_present": {
            "description": "Whether a billing address has been collected. The address itself is never returned here.",
            "type": "boolean"
          },
          "customer_email_present": {
            "description": "Whether an email has been collected. The email itself is never returned here — this says only that there is one.",
            "type": "boolean"
          },
          "country": {
            "description": "The country the checkout is being shown for.",
            "type": [
              "string",
              "null"
            ]
          },
          "order_reference": {
            "description": "Your reference for this order, or `null` when none was sent.",
            "type": [
              "string",
              "null"
            ]
          },
          "confirmation_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The session's own confirmation URL, or else its integration's."
          }
        }
      },
      "BrowserBlock": {
        "type": "object",
        "description": "The shopper's browser, for 3-D Secure. When present, the first eight fields are required.",
        "required": [
          "user_agent",
          "accept_header",
          "language",
          "colour_depth",
          "screen_height",
          "screen_width",
          "time_zone_offset_minutes",
          "javascript_enabled"
        ],
        "properties": {
          "user_agent": {
            "description": "The browser’s user-agent string.",
            "type": "string",
            "maxLength": 1024
          },
          "accept_header": {
            "description": "The browser’s Accept header.",
            "type": "string",
            "maxLength": 512
          },
          "language": {
            "description": "The browser’s language, for example `en-GB`.",
            "type": "string",
            "maxLength": 35
          },
          "colour_depth": {
            "description": "The screen’s colour depth in bits.",
            "type": "integer",
            "minimum": 1,
            "maximum": 48
          },
          "screen_height": {
            "description": "The screen height in pixels.",
            "type": "integer",
            "minimum": 1,
            "maximum": 20000
          },
          "screen_width": {
            "description": "The screen width in pixels.",
            "type": "integer",
            "minimum": 1,
            "maximum": 20000
          },
          "time_zone_offset_minutes": {
            "description": "The browser’s offset from UTC in minutes.",
            "type": "integer",
            "minimum": -840,
            "maximum": 840
          },
          "javascript_enabled": {
            "description": "Whether JavaScript is available.",
            "type": "boolean"
          },
          "java_enabled": {
            "description": "Whether Java is available. The card schemes still ask.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "trace": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "session_id"
            ],
            "properties": {
              "session_id": {
                "type": "string",
                "maxLength": 64
              },
              "device_id": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64
              }
            }
          },
          "channel": {
            "description": "How the payment is being made. Card schemes treat a browser differently from an app.",
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64
          },
          "sdk_version": {
            "description": "Which version of the checkout collected this.",
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64
          }
        }
      },
      "AuthenticateRequest": {
        "type": "object",
        "required": [
          "card_token",
          "card"
        ],
        "properties": {
          "card_token": {
            "type": "string",
            "description": "The reference the checkout's card fields produced. Never a card number."
          },
          "card": {
            "description": "What the checkout may say about the card. Never the number.",
            "type": "object",
            "properties": {
              "last4": {
                "description": "The last four digits, which the shopper recognises.",
                "type": "string"
              },
              "brand": {
                "description": "The card scheme.",
                "type": "string"
              },
              "expiry_month": {
                "description": "The expiry month, 1 to 12.",
                "type": "integer"
              },
              "expiry_year": {
                "description": "The four-digit expiry year.",
                "type": "integer"
              }
            }
          },
          "browser": {
            "description": "What the card scheme needs to know about the shopper’s browser to decide on a challenge.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/BrowserBlock"
              },
              {
                "type": "null"
              }
            ]
          },
          "billing_address": {
            "description": "The shopper’s billing address, when one was collected.",
            "type": [
              "object",
              "null"
            ],
            "required": [
              "line1",
              "postcode",
              "country"
            ],
            "properties": {
              "line1": {
                "description": "The first line of the address.",
                "type": "string",
                "maxLength": 128
              },
              "postcode": {
                "description": "The postcode.",
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9 -]{1,11}$"
              },
              "country": {
                "description": "A two-letter ISO 3166-1 country code.",
                "type": "string"
              }
            }
          }
        }
      },
      "ThreeDs": {
        "type": "object",
        "description": "Where the challenge is. `provider`, `session_id` and `mount` are left out when there is none.",
        "required": [
          "state"
        ],
        "properties": {
          "state": {
            "description": "Where this authentication has got to.",
            "type": "string",
            "enum": [
              "awaiting_challenge",
              "not_started"
            ]
          },
          "provider": {
            "description": "Which 3-D Secure provider is being used.",
            "type": "string"
          },
          "session_id": {
            "description": "The provider’s own id for this authentication.",
            "type": "string"
          },
          "mount": {
            "description": "What the checkout needs to put the provider’s challenge on the page.",
            "type": "object"
          }
        }
      },
      "AuthenticateResponse": {
        "type": "object",
        "required": [
          "status",
          "attempt_id",
          "three_ds",
          "amount_verified"
        ],
        "properties": {
          "status": {
            "description": "What to do next: the attempt is done, or it needs the shopper to authenticate.",
            "type": "string",
            "enum": [
              "requires_authentication"
            ]
          },
          "attempt_id": {
            "description": "This attempt. Quote it to us about this one try.",
            "type": "string"
          },
          "three_ds": {
            "description": "What the checkout needs in order to run 3-D Secure, or `null` when none is needed.",
            "$ref": "#/components/schemas/ThreeDs"
          },
          "amount_verified": {
            "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
            "type": "boolean"
          }
        }
      },
      "ConfirmRequest": {
        "type": "object",
        "properties": {
          "signals": {
            "type": "object",
            "description": "Behavioural signals the checkout collects, at most 16 KB."
          },
          "customer": {
            "description": "The shopper, when the checkout collected details the session did not already carry.",
            "type": "object",
            "properties": {
              "email": {
                "description": "The shopper’s email address.",
                "type": "string",
                "maxLength": 254
              }
            }
          }
        }
      },
      "ConfirmResult": {
        "type": "object",
        "required": [
          "status",
          "payment_id",
          "decline",
          "retry_after_seconds",
          "settlement",
          "amount_verified",
          "hold"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "captured",
              "held",
              "declined",
              "failed",
              "processing"
            ],
            "description": "`processing` while the outcome is not known yet; ask again after this response's own\n`retry_after_seconds`.\n\nA PAYMENT MAY BE IN FLIGHT. Asking again is a replay of the same request, never a new one, and\nstarting a second payment here is the mistake this field exists to prevent. An error body carries\na field of the same name meaning the opposite — that request created nothing and charged nothing,\nso sending it again is safe. Branch on the status and the code, not on the field name.\n\nOn every arm other than `processing` this field is written as `null` rather than omitted, which is\nthe reverse of the error body's convention.\n"
          },
          "payment_id": {
            "description": "The payment this confirm produced, or `null` when it produced none.",
            "type": [
              "string",
              "null"
            ]
          },
          "decline": {
            "description": "Why the payment was declined, or `null` when it was not.",
            "type": [
              "object",
              "null"
            ],
            "required": [
              "code",
              "category",
              "reason"
            ],
            "properties": {
              "code": {
                "description": "The stable decline code. Branch on this.",
                "type": "string"
              },
              "category": {
                "description": "How to treat the decline: `soft`, `hard`, `fraud`, `network` or `provider_error`. Soft is worth another attempt; hard is not.",
                "type": "string",
                "enum": [
                  "soft",
                  "hard",
                  "fraud",
                  "network",
                  "provider_error"
                ]
              },
              "reason": {
                "description": "The acquirer’s own words, passed on as received.",
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "retry_after_seconds": {
            "description": "How long to wait before asking again, in seconds, when the status is `processing`.\n\nA PAYMENT MAY BE IN FLIGHT. Asking again is a replay of the same request, never a new one. An error body\ncarries a field of the same name meaning the opposite — that request created nothing and charged nothing,\nso sending it again is safe. Branch on the status and the code, not on the field name.\n\nWritten as `null` on every arm other than `processing`, where the error body omits it instead.\n",
            "type": [
              "integer",
              "null"
            ]
          },
          "settlement": {
            "description": "Set while an attempt is finished but the outcome has not been reconciled with the acquirer yet; `null` otherwise. Money may already have moved, so do not release goods and do not treat the payment as failed while it is set — reconciliation resolves it, not a clock in the checkout.",
            "type": [
              "string",
              "null"
            ]
          },
          "amount_verified": {
            "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
            "type": "boolean"
          },
          "hold": {
            "description": "The hold placed on this payment, or `null` when none was placed.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/HoldBlock"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "AuthenticationIncompleteError": {
        "type": "object",
        "required": [
          "error",
          "three_ds",
          "request_id"
        ],
        "properties": {
          "error": {
            "description": "The error code. Branch on this, never on `message`.",
            "type": "string",
            "enum": [
              "authentication_incomplete"
            ]
          },
          "attempt_id": {
            "type": "string",
            "description": "Present when an attempt is waiting on its challenge."
          },
          "three_ds": {
            "description": "What is still needed to finish authenticating.",
            "$ref": "#/components/schemas/ThreeDs"
          },
          "message": {
            "description": "Help for a person reading logs. It may change; never branch on it.",
            "type": [
              "string",
              "null"
            ]
          },
          "request_id": {
            "description": "The same value as the `Rip-Request-Id` header.",
            "type": "string"
          }
        }
      },
      "CheckoutConfig": {
        "type": "object",
        "required": [
          "merchant",
          "mode",
          "payment_methods",
          "capture",
          "three_ds",
          "presentation",
          "integration",
          "currency",
          "config_version"
        ],
        "properties": {
          "merchant": {
            "description": "Who the shopper is paying, as the checkout shows them.",
            "type": "object",
            "required": [
              "display_name",
              "handle",
              "public_key"
            ],
            "properties": {
              "display_name": {
                "description": "The trading name the shopper sees.",
                "type": "string"
              },
              "handle": {
                "description": "Your handle, as registered.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "public_key": {
                "description": "The publishable key this config was fetched with.",
                "type": "string"
              }
            }
          },
          "mode": {
            "description": "`test` or `live`. A test config can take only test cards.",
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "payment_methods": {
            "description": "The payment methods the checkout may offer.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "capture": {
            "type": "object",
            "description": "How the card fields load.",
            "required": [
              "sdk_url",
              "public_token",
              "init_params",
              "token_format"
            ],
            "properties": {
              "sdk_url": {
                "description": "Where the checkout loads the card-capture code from.",
                "type": "string"
              },
              "public_token": {
                "description": "The token the card-capture code authenticates with. It cannot take a payment on its own.",
                "type": "string"
              },
              "init_params": {
                "description": "What the card-capture code needs to start.",
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "token_format": {
                "description": "The shape of the token the capture returns.",
                "type": "string"
              }
            }
          },
          "three_ds": {
            "description": "What the checkout needs in order to run 3-D Secure.",
            "type": "object",
            "required": [
              "provider"
            ],
            "properties": {
              "provider": {
                "description": "Which 3-D Secure provider this integration uses.",
                "type": "string"
              }
            }
          },
          "presentation": {
            "type": "object",
            "description": "How the checkout looks, after the integration's own settings are applied.",
            "required": [
              "brand",
              "layout",
              "copy",
              "extensions",
              "payment_link",
              "custom_css",
              "presentation_version",
              "button_label"
            ],
            "properties": {
              "brand": {
                "description": "How the checkout is dressed.",
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "logo_url": {
                    "description": "Your logo, shown at the top of the checkout.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "primary_colour": {
                    "description": "The colour the checkout uses for its main action.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "accent_colour": {
                    "description": "The colour the checkout uses to draw attention to a second thing.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "font_family": {
                    "description": "The typeface the checkout sets its text in.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "favicon_url": {
                    "description": "The icon for the hosted payment page’s tab.",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "layout": {
                "description": "How the checkout arranges itself.",
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "card_field_layout": {
                    "description": "Whether the card fields sit on one line or several.",
                    "type": "string",
                    "enum": [
                      "single_line",
                      "stacked"
                    ]
                  },
                  "method_display": {
                    "description": "How the payment methods are presented.",
                    "type": "string",
                    "enum": [
                      "tabs",
                      "accordion",
                      "list"
                    ]
                  },
                  "step_mode": {
                    "description": "Whether the checkout is one page or a sequence of steps.",
                    "type": "string",
                    "enum": [
                      "single_page",
                      "multi_step"
                    ]
                  },
                  "density": {
                    "description": "How much space the checkout leaves around its fields.",
                    "type": "string",
                    "enum": [
                      "compact",
                      "standard",
                      "spacious"
                    ]
                  },
                  "field_order": {
                    "description": "The order the fields are shown in.",
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "copy": {
                "description": "Words of yours that replace the checkout’s own.",
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "cta_text": {
                    "description": "What the pay button says.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "trust_badge_text": {
                    "description": "What the trust badge says.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "error_messages": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "footer_text": {
                    "description": "What sits below the form.",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "extensions": {
                "description": "Parts of the checkout you can turn on or off.",
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "show_order_summary": {
                    "description": "Whether the basket is shown beside the form.",
                    "type": "boolean"
                  },
                  "show_trust_badges": {
                    "description": "Whether the trust badges are shown.",
                    "type": "boolean"
                  }
                }
              },
              "payment_link": {
                "description": "How the hosted payment page presents itself, when you use one.",
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "hero_image_url": {
                    "description": "The image at the top of the hosted page.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "headline": {
                    "description": "The heading on the hosted page.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "description": {
                    "description": "The text below the heading on the hosted page.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "show_merchant_logo": {
                    "description": "Whether your logo is shown on the hosted page.",
                    "type": "boolean"
                  }
                }
              },
              "custom_css": {
                "description": "Your own CSS, applied to the checkout.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "presentation_version": {
                "description": "Changes whenever the presentation does, so a cached copy can be checked.",
                "type": "integer"
              },
              "button_label": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "integration": {
            "description": "The integration this config belongs to and the rules it carries.",
            "type": "object",
            "required": [
              "name",
              "kind",
              "theme",
              "mode",
              "amount_trust_policy",
              "hold_duration_days",
              "signals_capture_enabled",
              "confirmation_behaviour",
              "defaults",
              "confirmation_url"
            ],
            "properties": {
              "name": {
                "description": "The integration’s name, as you registered it.",
                "type": "string"
              },
              "kind": {
                "description": "What kind of integration this is.",
                "type": "string",
                "enum": [
                  "default",
                  "hosted_page",
                  "custom"
                ]
              },
              "theme": {
                "description": "Which theme the checkout draws.",
                "type": "string",
                "enum": [
                  "light",
                  "dark",
                  "auto"
                ]
              },
              "mode": {
                "type": "string",
                "enum": [
                  "auto",
                  "test"
                ],
                "description": "`test` keeps every session in test mode. `auto` follows your account."
              },
              "amount_trust_policy": {
                "description": "How far ripper trusts an amount the page supplies.",
                "type": "string",
                "enum": [
                  "allow_unverified",
                  "require_verified"
                ]
              },
              "hold_duration_days": {
                "description": "How long a held payment waits for a decision before it is released.",
                "type": "integer",
                "minimum": 1,
                "maximum": 5
              },
              "signals_capture_enabled": {
                "description": "Whether the checkout collects behavioural signals for this integration.",
                "type": "boolean"
              },
              "confirmation_behaviour": {
                "description": "What the checkout does once a payment finishes.",
                "type": "string",
                "enum": [
                  "show_confirmation",
                  "resolve_only"
                ]
              },
              "defaults": {
                "description": "What a page-created session gets when it names no amount or basket of its own.",
                "type": "object",
                "required": [
                  "amount",
                  "currency",
                  "items"
                ],
                "properties": {
                  "amount": {
                    "description": "The default amount, in the currency’s minor unit.",
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "currency": {
                    "description": "The default currency.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "items": {
                    "description": "The default basket. This is the browser config’s line shape, which carries no `kind` and no tax — a merchant basket sent from your server is a different, richer line.",
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "object",
                      "required": [
                        "sku",
                        "name",
                        "quantity",
                        "unit_amount"
                      ],
                      "properties": {
                        "sku": {
                          "description": "Your code for the item.",
                          "type": "string"
                        },
                        "name": {
                          "description": "What the shopper sees.",
                          "type": "string"
                        },
                        "quantity": {
                          "description": "How many.",
                          "type": "integer"
                        },
                        "unit_amount": {
                          "description": "What the shopper pays per unit, in the currency’s minor unit.",
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              },
              "confirmation_url": {
                "description": "Where the shopper is sent after the payment, unless a session overrides it.",
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "currency": {
            "description": "The currency this integration charges in.",
            "type": [
              "string",
              "null"
            ]
          },
          "config_version": {
            "description": "Changes whenever this config does, so a cached copy can be checked.",
            "type": "integer"
          }
        }
      },
      "CheckoutPaymentMethods": {
        "type": "object",
        "required": [
          "methods"
        ],
        "properties": {
          "methods": {
            "description": "The payment methods this key can take.",
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "type",
                "brands"
              ],
              "properties": {
                "type": {
                  "description": "The method.",
                  "type": "string"
                },
                "brands": {
                  "description": "The card schemes accepted.",
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "StripeCreateBody": {
        "type": "object",
        "required": [
          "amount",
          "currency"
        ],
        "description": "The parameters the Stripe-compatible create supports. Any other parameter is refused by name.",
        "properties": {
          "amount": {
            "type": "integer",
            "description": "Minor units."
          },
          "currency": {
            "type": "string",
            "description": "Three letters, any case."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Sent as `metadata[key]=value` in form encoding. If your integration names an order-reference metadata key, that value becomes the order reference."
          },
          "receipt_email": {
            "type": "string",
            "description": "The shopper's email. Every ripper payment needs one."
          },
          "description": {
            "type": "string",
            "description": "Kept as `metadata.description`."
          },
          "statement_descriptor_suffix": {
            "description": "Added to the name on the shopper’s card statement.",
            "type": "string"
          },
          "capture_method": {
            "type": "string",
            "enum": [
              "automatic"
            ],
            "description": "Only `automatic` is supported."
          }
        }
      },
      "PaymentIntent": {
        "type": "object",
        "required": [
          "id",
          "object",
          "amount",
          "currency",
          "status",
          "client_secret",
          "created",
          "livemode",
          "metadata",
          "statement_descriptor_suffix",
          "receipt_email",
          "capture_method",
          "last_payment_error",
          "next_action",
          "ripper",
          "cancellation_reason"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The ripper session id."
          },
          "object": {
            "description": "Always `payment_intent`, as Stripe sends it.",
            "type": "string",
            "enum": [
              "payment_intent"
            ]
          },
          "amount": {
            "description": "What will be charged, in the currency’s smallest unit.",
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "description": "Lower case, as Stripe spells it."
          },
          "status": {
            "description": "The intent’s status, in Stripe’s vocabulary rather than ripper’s.",
            "type": "string",
            "enum": [
              "requires_payment_method",
              "requires_action",
              "processing",
              "requires_capture",
              "succeeded",
              "canceled"
            ]
          },
          "client_secret": {
            "type": [
              "string",
              "null"
            ],
            "description": "The ripper client secret on create; null on a read."
          },
          "created": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "livemode": {
            "description": "`false` in test mode, as Stripe sends it.",
            "type": "boolean"
          },
          "metadata": {
            "description": "Your own values, keys exactly as you sent them.",
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "statement_descriptor_suffix": {
            "description": "Added to the name on the shopper’s card statement.",
            "type": [
              "string",
              "null"
            ]
          },
          "receipt_email": {
            "description": "Where a receipt would be sent.",
            "type": [
              "string",
              "null"
            ]
          },
          "capture_method": {
            "description": "Whether the payment is captured straight away or left authorised.",
            "type": "string",
            "enum": [
              "automatic"
            ]
          },
          "last_payment_error": {
            "description": "The most recent failure on this intent, or `null`.",
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "description": "Stripe’s error code.",
                "type": "string"
              },
              "decline_code": {
                "description": "Stripe’s decline code, when the issuer gave one.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "message": {
                "description": "Help for a person reading logs. It may change; never branch on it.",
                "type": "string"
              }
            }
          },
          "next_action": {
            "description": "What the shopper must do next, or `null` when nothing is needed.",
            "type": "null"
          },
          "ripper": {
            "description": "The parts of this payment Stripe has no field for. Everything else on this object is Stripe’s shape.",
            "type": "object",
            "required": [
              "checkout_session_id",
              "amount_verified",
              "hold"
            ],
            "properties": {
              "checkout_session_id": {
                "description": "The ripper session behind this intent.",
                "type": "string"
              },
              "amount_verified": {
                "description": "Whether ripper could check the amount against your price list before charging it. When it could not, an approved payment is held for you to review rather than captured.",
                "type": "boolean"
              },
              "hold": {
                "description": "The hold on this payment, or `null` when there is none. Stripe has no equivalent.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/HoldBlock"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          },
          "cancellation_reason": {
            "description": "Why the intent was cancelled, or `null`.",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "automatic",
              "abandoned",
              null
            ]
          }
        }
      },
      "StripeErrorEnvelope": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "description": "The error, in Stripe’s envelope.",
            "type": "object",
            "required": [
              "type",
              "code",
              "message",
              "param",
              "ripper_code"
            ],
            "properties": {
              "type": {
                "description": "Stripe’s error type.",
                "type": "string",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "permission_error",
                  "api_error"
                ]
              },
              "code": {
                "description": "Stripe’s error code.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "message": {
                "description": "Help for a person reading logs. It may change; never branch on it.",
                "type": "string"
              },
              "param": {
                "description": "Which parameter was at fault, when one was.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ripper_code": {
                "description": "The ripper error code behind this one, so you can look it up in our reference.",
                "$ref": "#/components/schemas/ErrorCode"
              }
            }
          }
        }
      }
    }
  },
  "x-ripper-source-hash": "445d9a5053d5fde3",
  "x-ripper-source-commit": "54b37e17b03247b26046a9341b7afd743e1e3730"
}
