{
  "openapi": "3.0.3",
  "info": {
    "title": "ShiftForest browser billing services",
    "version": "2026-10-31",
    "description": "Account application endpoints requiring an active administrator Supabase bearer session. Workforce integration keys are not accepted. Except Stripe webhook, requests with Origin must match the configured application origin. Responses are private/no-store. Prices are GBP. Live checkout is activated for the configured seller; runtime kill switches remain enforced. Provider idempotency and worker leases are internal; Idempotency-Key headers do not replace quote_id/request_id. See repository docs/implementation-2026-10-28/BILLING_CHANGES.md."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "security": [
    {
      "AdminSession": []
    }
  ],
  "paths": {
    "/api/billing/status": {
      "get": {
        "operationId": "billingStatus",
        "description": "Session administrator only. Reconciled entitlements and up to 20 confirmed recent changes, newest first. Drafts are omitted. No provider calls.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/billing/workers": {
      "get": {
        "operationId": "billingWorkers",
        "description": "Session administrator only. Workers consuming allocations or assigned future shifts. UUID keyset pagination; page size 100. Release requires no future shifts and no open attendance.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkerPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Use next_cursor verbatim. Omit for the first page."
          }
        ]
      }
    },
    "/api/billing/release-worker": {
      "post": {
        "operationId": "billingReleaseWorker",
        "description": "Release an unused allocation; server rechecks open attendance and future shifts under a tenant lock. Historical records remain. No subscription price or paid capacity change.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "released": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "released"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "user_id"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/api/billing/change-preview": {
      "post": {
        "operationId": "billingPreviewChange",
        "description": "Create an unconfirmed ten-minute preview. No subscription mutation. Card/Link, active paid standard subscriptions only. Same-frequency higher-price changes with no capacity reduction are immediate prorated invoices; reductions, mixed or frequency changes start at renewal. Amount due now includes current tax/credits for immediate changes; recurring_minor is before tax. Renewal tax/credits are determined at renewal. Preview ID is tenant-bound.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Change"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Selection"
              }
            }
          }
        }
      }
    },
    "/api/billing/change-confirm": {
      "post": {
        "operationId": "billingConfirmChange",
        "description": "Explicitly confirm a preview after showing its price and timing. Server rechecks subscription, immediate charge and worker allocations. One change per tenant may be in flight. Reuse quote_id for ambiguous retries and read status; do not create a replacement. Server persists provider commands/idempotency keys. Unpaid immediate changes retain existing capacity. Confirmed reductions reserve the lower capacity immediately for new assignments, while paid entitlement changes at renewal. Processing may return 409 or uncertain 503; reconciliation continues.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Change"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeId"
              }
            }
          }
        }
      }
    },
    "/api/billing/change-cancel": {
      "post": {
        "operationId": "billingCancelChange",
        "description": "Release a scheduled future change more than five minutes before renewal. This does not cancel the subscription or reverse an already effective change. Only scheduled/canceling/canceled changes accepted. Not a pending-invoice cancellation endpoint. Reuse quote_id after uncertainty and read status.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Change"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeId"
              }
            }
          }
        }
      }
    },
    "/api/billing/checkout": {
      "post": {
        "operationId": "billingCheckout",
        "description": "Open/reopen hosted Checkout for a new eligible subscription. Persist request_id before calling and reuse with identical selection after uncertain responses. One-hour checkout window. Tenant/customer/price IDs and return URLs are server-controlled. A redirect does not confirm payment. Hosted Checkout requires a business/organisation name and a business-use purchase declaration. These are retained on the Stripe session and do not verify tax status.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "quote": {
                      "$ref": "#/components/schemas/Quote"
                    }
                  },
                  "required": [
                    "url",
                    "quote"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan": {
                    "type": "string",
                    "enum": [
                      "team",
                      "operations",
                      "care"
                    ]
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "month",
                      "year"
                    ]
                  },
                  "capacity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10000,
                    "description": "Total worker capacity. Rounded up to the selected plan minimum: Team 15, Operations 20, Care 30."
                  },
                  "request_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "plan",
                  "interval",
                  "capacity",
                  "request_id"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/api/billing/portal": {
      "post": {
        "operationId": "billingPortal",
        "description": "Open the mapped tenant customer portal. Provider configuration must disable subscription_update; use change-preview/confirm for changes. No caller customer IDs or return URLs.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "required": [
                    "url"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {},
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/api/billing/webhook": {
      "post": {
        "operationId": "billingWebhook",
        "description": "Stripe-only ingress. Verify exact raw body with the endpoint secret and a 300-second signature tolerance; test/live mode must match. Known-customer invoice/subscription/Checkout events are durably deduplicated wake-up signals. Respond 200 only after persistence; ignore unsupported/unknown-customer events. Stripe should retry non-2xx responses. Maximum body 1 MiB.",
        "responses": {
          "200": {
            "description": "Success. Provider payment is not implied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "received"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "Stripe-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/billing/invoices": {
      "get": {
        "operationId": "billingInvoices",
        "description": "Active selected-workspace administrator session only; workforce integration keys are rejected. Customer identity is resolved server-side. Reads Stripe without creating invoices, payments or messages. Cursor must belong to the same customer and billing mode. Refresh first page if a cursor is deleted. Draft and final invoices included; GBP only. No provider contact, address, tax ID or metadata fields returned. Unknown and duplicate query parameters rejected. Links are private; never log or publish them.",
        "parameters": [
          {
            "in": "header",
            "name": "X-ShiftForest-Workspace",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Selected workspace. SDK supplies this explicitly; omission uses the existing default membership selection."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string",
              "pattern": "^in_[A-Za-z0-9]{1,200}$"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current provider invoice records, newest first. Empty when the workspace has no billing customer. This is not a consistent historical snapshot or refund ledger.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoicePage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/billing/features": {
      "get": {
        "operationId": "billingFeatures",
        "description": "Selected-workspace active administrator only. No provider call or client-selected customer. No query parameters accepted. Bank/agency recovery and new staffing evidence packs require Care for enforced paid accounts; pilots and valid trials retain access. Existing records, internal recovery, clock-out and corrections are not blocked by these feature gates. All existing role, workplace and safety permissions still apply. Worker capacity and schedule-expansion expiry are independent.",
        "parameters": [
          {
            "in": "header",
            "name": "X-ShiftForest-Workspace",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current server policy. Does not grant roles or capacity and is not a reservation; rechecked transactionally on creation. Core availability describes continuity, not permission to expand an expired schedule.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingFeatures"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/billing/payments": {
      "get": {
        "operationId": "billingPayments",
        "description": "Active selected-workspace administrator session only. Customer resolved server-side; workforce API keys rejected. Read-only Stripe observations, GBP integer pence, no provider contact/bank details or metadata. Unknown/duplicate parameters rejected. Cursor and each record checked against the customer and billing mode. This is not a frozen accounting statement or payout report. Credit notes may refer to refunds already listed as payments; do not sum both. No money movement or email sending.",
        "parameters": [
          {
            "in": "header",
            "name": "X-ShiftForest-Workspace",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Selected workspace. SDK supplies this explicitly; omission uses the existing default membership selection."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string",
              "pattern": "^ch_[A-Za-z0-9]{1,200}$"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded newest-first provider records. Payments and credits return an empty page without a billing customer; refunds require an owned payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/billing/refunds": {
      "get": {
        "operationId": "billingRefunds",
        "description": "Active selected-workspace administrator session only. Customer resolved server-side; workforce API keys rejected. Read-only Stripe observations, GBP integer pence, no provider contact/bank details or metadata. Unknown/duplicate parameters rejected. Cursor and each record checked against the customer and billing mode. This is not a frozen accounting statement or payout report. Credit notes may refer to refunds already listed as payments; do not sum both. No money movement or email sending.",
        "parameters": [
          {
            "in": "header",
            "name": "X-ShiftForest-Workspace",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Selected workspace. SDK supplies this explicitly; omission uses the existing default membership selection."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string",
              "pattern": "^re_[A-Za-z0-9]{1,200}$"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "in": "query",
            "name": "payment_id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^ch_[A-Za-z0-9]{1,200}$"
            },
            "description": "An owned payment from /api/billing/payments. Its customer and billing mode are checked before refund retrieval; cursor and returned rows must reference this payment."
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded newest-first provider records. Payments and credits return an empty page without a billing customer; refunds require an owned payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/billing/credits": {
      "get": {
        "operationId": "billingCredits",
        "description": "Active selected-workspace administrator session only. Customer resolved server-side; workforce API keys rejected. Read-only Stripe observations, GBP integer pence, no provider contact/bank details or metadata. Unknown/duplicate parameters rejected. Cursor and each record checked against the customer and billing mode. This is not a frozen accounting statement or payout report. Credit notes may refer to refunds already listed as payments; do not sum both. No money movement or email sending.",
        "parameters": [
          {
            "in": "header",
            "name": "X-ShiftForest-Workspace",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Selected workspace. SDK supplies this explicitly; omission uses the existing default membership selection."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string",
              "pattern": "^cn_[A-Za-z0-9]{1,200}$"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded newest-first provider records. Payments and credits return an empty page without a billing customer; refunds require an owned payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input/signature. Fix the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or expired session. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Active tenant administrator required or origin denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale preview, operation in progress, or conflicting subscription/worker state. Refresh status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Configuration/dependency unavailable or mutation outcome uncertain. Refresh status; never infer that payment failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "JSON content type required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "AdminSession": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Supabase access JWT"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "Selection": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "team",
              "operations",
              "care"
            ]
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "capacity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "description": "Total worker capacity. Rounded up to the selected plan minimum: Team 15, Operations 20, Care 30."
          }
        },
        "required": [
          "plan",
          "interval",
          "capacity"
        ],
        "additionalProperties": false
      },
      "Change": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "plan": {
            "type": "string",
            "enum": [
              "team",
              "operations",
              "care"
            ]
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "capacity": {
            "type": "integer"
          },
          "timing": {
            "type": "string",
            "enum": [
              "immediate",
              "renewal"
            ]
          },
          "effective_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "state": {
            "type": "string",
            "enum": [
              "draft",
              "applying",
              "canceling",
              "submitted",
              "payment_pending",
              "scheduled",
              "applied",
              "expired",
              "canceled",
              "review_required"
            ]
          },
          "preview": {
            "type": "object",
            "properties": {
              "currency": {
                "type": "string",
                "enum": [
                  "gbp"
                ]
              },
              "amount_due_now": {
                "type": "integer",
                "description": "GBP minor units (pence)."
              },
              "recurring_minor": {
                "type": "integer",
                "description": "Recurring price before tax in pence."
              },
              "subtotal": {
                "type": "integer",
                "description": "GBP minor units (pence).",
                "nullable": true
              },
              "total": {
                "type": "integer",
                "description": "GBP minor units (pence).",
                "nullable": true
              }
            },
            "required": [
              "currency",
              "amount_due_now",
              "recurring_minor",
              "subtotal",
              "total"
            ],
            "additionalProperties": false
          },
          "result": {
            "type": "object",
            "properties": {
              "payment_url": {
                "type": "string",
                "format": "uri"
              },
              "pending_expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "schedule_id": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "plan",
          "interval",
          "capacity",
          "timing",
          "effective_at",
          "expires_at",
          "created_at",
          "state",
          "preview",
          "result"
        ],
        "additionalProperties": false
      },
      "Quote": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "team",
              "operations",
              "care"
            ]
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "capacity": {
            "type": "integer"
          },
          "included": {
            "type": "integer"
          },
          "extra": {
            "type": "integer"
          },
          "baseMinor": {
            "type": "integer",
            "description": "GBP minor units (pence)."
          },
          "extraUnitMinor": {
            "type": "integer",
            "description": "GBP minor units (pence)."
          },
          "totalMinor": {
            "type": "integer",
            "description": "GBP minor units (pence)."
          },
          "currency": {
            "type": "string",
            "enum": [
              "gbp"
            ]
          }
        },
        "required": [
          "plan",
          "interval",
          "capacity",
          "included",
          "extra",
          "baseMinor",
          "extraUnitMinor",
          "totalMinor",
          "currency"
        ],
        "additionalProperties": false
      },
      "Status": {
        "type": "object",
        "properties": {
          "company_id": {
            "type": "string",
            "format": "uuid"
          },
          "plan": {
            "type": "string",
            "enum": [
              "team",
              "operations",
              "care"
            ]
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "capacity": {
            "type": "integer"
          },
          "used": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pilot",
              "trialing",
              "active",
              "past_due",
              "unpaid",
              "canceled",
              "incomplete",
              "incomplete_expired",
              "paused"
            ]
          },
          "period_end": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "trial_end": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "grace_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "has_customer": {
            "type": "boolean"
          },
          "has_subscription": {
            "type": "boolean"
          },
          "enforcement": {
            "type": "boolean"
          },
          "livemode": {
            "type": "boolean"
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "changes": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/Change"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "test_mode": {
            "type": "boolean"
          },
          "plans": {
            "type": "object",
            "properties": {
              "team": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "monthlyBase": {
                    "type": "integer",
                    "description": "GBP minor units (pence)."
                  },
                  "included": {
                    "type": "integer"
                  },
                  "monthlyExtra": {
                    "type": "integer",
                    "description": "GBP minor units (pence)."
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "monthlyBase",
                  "included",
                  "monthlyExtra",
                  "description"
                ],
                "additionalProperties": false
              },
              "operations": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "monthlyBase": {
                    "type": "integer",
                    "description": "GBP minor units (pence)."
                  },
                  "included": {
                    "type": "integer"
                  },
                  "monthlyExtra": {
                    "type": "integer",
                    "description": "GBP minor units (pence)."
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "monthlyBase",
                  "included",
                  "monthlyExtra",
                  "description"
                ],
                "additionalProperties": false
              },
              "care": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "monthlyBase": {
                    "type": "integer",
                    "description": "GBP minor units (pence)."
                  },
                  "included": {
                    "type": "integer"
                  },
                  "monthlyExtra": {
                    "type": "integer",
                    "description": "GBP minor units (pence)."
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "monthlyBase",
                  "included",
                  "monthlyExtra",
                  "description"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "team",
              "operations",
              "care"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "company_id",
          "plan",
          "interval",
          "capacity",
          "used",
          "status",
          "period_end",
          "trial_end",
          "grace_until",
          "cancel_at_period_end",
          "has_customer",
          "has_subscription",
          "enforcement",
          "livemode",
          "last_synced_at",
          "changes",
          "enabled",
          "test_mode",
          "plans"
        ],
        "additionalProperties": false
      },
      "ChangeId": {
        "type": "object",
        "properties": {
          "quote_id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "quote_id"
        ],
        "additionalProperties": false
      },
      "WorkerPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "future_shifts": {
                  "type": "boolean"
                },
                "open_attendance": {
                  "type": "boolean"
                }
              },
              "required": [
                "id",
                "name",
                "future_shifts",
                "open_attendance"
              ],
              "additionalProperties": false
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        },
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "additionalProperties": false
      },
      "Invoice": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "number",
          "status",
          "currency",
          "total",
          "amount_paid",
          "amount_remaining",
          "created_at",
          "due_at",
          "hosted_invoice_url",
          "invoice_pdf"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^in_[A-Za-z0-9]{1,200}$"
          },
          "number": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "open",
              "paid",
              "uncollectible",
              "void"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "gbp"
            ]
          },
          "total": {
            "type": "integer",
            "description": "GBP minor units (pence). Invoice amounts; not a net refund/credit ledger."
          },
          "amount_paid": {
            "type": "integer",
            "description": "GBP minor units (pence). Invoice amounts; not a net refund/credit ledger."
          },
          "amount_remaining": {
            "type": "integer",
            "description": "GBP minor units (pence). Invoice amounts; not a net refund/credit ledger."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "hosted_invoice_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Private Stripe link. Do not log or persist in public caches. Null when not available."
          },
          "invoice_pdf": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Private Stripe link. Do not log or persist in public caches. Null when not available."
          }
        }
      },
      "InvoicePage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor",
          "has_more",
          "limit"
        ],
        "properties": {
          "data": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Invoice"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          }
        }
      },
      "BillingFeatures": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "version",
          "company_id",
          "features"
        ],
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "2026-10-31"
            ]
          },
          "company_id": {
            "type": "string",
            "format": "uuid"
          },
          "features": {
            "type": "array",
            "minItems": 6,
            "maxItems": 6,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "required_plan",
                "can_start",
                "reason",
                "existing_records_available"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "core_workspace",
                    "bank_recovery",
                    "staffing_evidence",
                    "specialist_payroll",
                    "delegated_roles",
                    "enterprise_identity"
                  ]
                },
                "required_plan": {
                  "type": "string",
                  "enum": [
                    "team",
                    "operations",
                    "care"
                  ]
                },
                "can_start": {
                  "type": "boolean"
                },
                "reason": {
                  "type": "string",
                  "enum": [
                    "included",
                    "supported_pilot",
                    "trial",
                    "upgrade_required",
                    "subscription_attention"
                  ]
                },
                "existing_records_available": {
                  "type": "boolean",
                  "enum": [
                    true
                  ]
                }
              }
            }
          }
        }
      },
      "Payment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "currency",
          "amount",
          "created_at",
          "amount_captured",
          "amount_refunded",
          "disputed"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ch_[A-Za-z0-9]{1,200}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "pending",
              "failed"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "gbp"
            ]
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "amount_captured": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "amount_refunded": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "disputed": {
            "type": "boolean"
          }
        }
      },
      "PaymentPage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor",
          "has_more",
          "limit"
        ],
        "properties": {
          "data": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Payment"
            }
          },
          "next_cursor": {
            "type": "string",
            "pattern": "^ch_[A-Za-z0-9]{1,200}$",
            "nullable": true
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          }
        }
      },
      "Refund": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "payment_id",
          "status",
          "currency",
          "amount",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^re_[A-Za-z0-9]{1,200}$"
          },
          "payment_id": {
            "type": "string",
            "pattern": "^ch_[A-Za-z0-9]{1,200}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "requires_action",
              "succeeded",
              "failed",
              "canceled"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "gbp"
            ]
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RefundPage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor",
          "has_more",
          "limit"
        ],
        "properties": {
          "data": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Refund"
            }
          },
          "next_cursor": {
            "type": "string",
            "pattern": "^re_[A-Za-z0-9]{1,200}$",
            "nullable": true
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          }
        }
      },
      "Credit": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "invoice_id",
          "number",
          "status",
          "currency",
          "amount",
          "created_at",
          "pre_payment_amount",
          "post_payment_amount",
          "out_of_band_amount",
          "pdf"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^cn_[A-Za-z0-9]{1,200}$"
          },
          "invoice_id": {
            "type": "string",
            "pattern": "^in_[A-Za-z0-9]{1,200}$"
          },
          "number": {
            "type": "string",
            "maxLength": 200
          },
          "status": {
            "type": "string",
            "enum": [
              "issued",
              "void"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "gbp"
            ]
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "pre_payment_amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "post_payment_amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "out_of_band_amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "pdf": {
            "type": "string",
            "format": "uri",
            "nullable": true
          }
        }
      },
      "CreditPage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor",
          "has_more",
          "limit"
        ],
        "properties": {
          "data": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Credit"
            }
          },
          "next_cursor": {
            "type": "string",
            "pattern": "^cn_[A-Za-z0-9]{1,200}$",
            "nullable": true
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          }
        }
      }
    }
  }
}
