{
  "openapi": "3.1.0",
  "info": {
    "title": "Infrastructure Payments API",
    "version": "1.0.0",
    "description": "One API in front of payment gateways (Stripe end to end; Mercado Pago and Hotmart stored but not implemented), with projects, gateways, plans, features, subscriptions, checkout, the customer portal and transactional e-mails.\n\nMental model: a project holds gateways; a gateway holds plans; a user subscribes to a plan and gains the features linked to it.\n\nCredentials: an Infrastructure Auth token identifies a PERSON (dashboard, agents); a project secret key `psk_…` identifies ONE project (server to server); a project public key `pk_…` identifies a project from the browser and only reaches `/sdk/v1/*`. `/checkout` and `/account` are public. Both bearer credentials travel in `Authorization: Bearer …`.\n\nContract: resources at the root; collections as `{items, page:{limit, offset, total, hasMore}}`; errors as `{error:{code, message, details?}}`. Prices are in cents.",
    "contact": {
      "name": "Infrastructure",
      "url": "https://myinfrastructure.click/contact"
    },
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://payments.worker.myinfrastructure.click"
    }
  ],
  "externalDocs": {
    "description": "Agent-ready implementation guide",
    "url": "https://myinfrastructure.click/products/payments/llms.txt"
  },
  "tags": [
    {
      "name": "Status",
      "description": "Public service health."
    },
    {
      "name": "Account",
      "description": "The caller’s Payments account."
    },
    {
      "name": "Projects",
      "description": "Projects and their keys."
    },
    {
      "name": "Gateways",
      "description": "Payment provider credentials of a project."
    },
    {
      "name": "Plans",
      "description": "Priced plans of a gateway."
    },
    {
      "name": "Features",
      "description": "Access slugs and their links to plans."
    },
    {
      "name": "Subscriptions",
      "description": "Subscriptions recorded from provider webhooks."
    },
    {
      "name": "Checkout",
      "description": "Checkout, customer portal and customers."
    },
    {
      "name": "SDK",
      "description": "Browser-safe routes used by `@ciromaciel/payments-react`."
    },
    {
      "name": "Emails",
      "description": "Transactional e-mail branding, templates and log."
    },
    {
      "name": "Webhooks",
      "description": "Receivers called by payment providers."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "oauth2": []
    },
    {
      "projectSecretKey": []
    }
  ],
  "paths": {
    "/status": {
      "get": {
        "operationId": "getServiceStatus",
        "tags": [
          "Status"
        ],
        "summary": "Service health",
        "description": "Public, anonymous health report. Probes the database and the credential-encryption key and returns the worst status among them. Never exposes business volume. Served with `Cache-Control: no-store`.",
        "security": [],
        "responses": {
          "200": {
            "description": "Health report (also returned when a dependency is down — the status is in the body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceStatus"
                }
              }
            }
          }
        }
      }
    },
    "/plan": {
      "get": {
        "operationId": "getPlanNotice",
        "tags": [
          "Account"
        ],
        "summary": "Billing notice for the caller",
        "description": "Returns the caller's Payments subscription state as `{state, plan, limit, message}` — the same shape the dashboards of the other products read. `state` is `active`, `free`, `none` (over the free tier: `message` explains) or `unknown` (the plan could not be read). Meaningful for a person (JWT); the code does not refuse a project secret key but has no owner to evaluate for it.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Plan notice",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanNotice"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/me/deletion-preview": {
      "get": {
        "operationId": "getAccountDeletionPreview",
        "tags": [
          "Account"
        ],
        "summary": "What deleting the account would affect",
        "description": "Counts what the caller holds in Payments (projects, gateways, plans, subscriptions, active subscriptions). Read-only. Requires an Auth token; a project secret key gets 403.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Inventory",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountInventory"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/me": {
      "delete": {
        "operationId": "deleteAccount",
        "tags": [
          "Account"
        ],
        "summary": "Delete the account’s Payments data",
        "description": "Refuses with 409 while any project remains (delete each with `DELETE /projects/{id}` first; active subscriptions keep charging at the gateway). With no projects there is nothing to delete here — the account itself lives in Infrastructure Auth. Requires an Auth token.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Nothing left in Payments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "$ref": "#/components/schemas/AccountInventory"
                    }
                  },
                  "required": [
                    "deleted"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Projects still exist. `error.code` is `CONFLICT`; `error.details` carries `reason: \"has_resources\"` and the inventory counts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Projects"
        ],
        "summary": "List your projects",
        "description": "Lists the caller's projects. Secret keys are never returned — only `secretKeyPrefix`. Not paginated: `page.total` and `page.hasMore` are `null` (nobody counted). Requires an Auth token; a project secret key gets 403.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createProject",
        "tags": [
          "Projects"
        ],
        "summary": "Create a project",
        "description": "Creates a project owned by the token’s subject and mints its keys. The plaintext `secretKey` (`psk_…`) appears ONLY in this response — store it. Subject to the Payments plan’s project limit (402). Requires an Auth token.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Project name.",
                    "minLength": 1
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created, with the one-time plaintext secret key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedProject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Project limit of the plan reached. `error.code` is `PLAN_LIMIT`; `error.details` carries `code` (`PLAN_LIMIT` or `SUBSCRIPTION_REQUIRED`), `reason`, `resource`, `current`, `limit`, `plan`, `requiredPlan`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/overview": {
      "get": {
        "operationId": "getProjectsOverview",
        "tags": [
          "Projects"
        ],
        "summary": "Account overview per project",
        "description": "One aggregate row per project: gateway summary (`empty`/`test`/`live`), plan count, plans not synced to the provider, feature count. Returns a bare JSON array (not a collection envelope). Requires an Auth token.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "Overview rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProjectOverview"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}": {
      "patch": {
        "operationId": "updateProject",
        "tags": [
          "Projects"
        ],
        "summary": "Rename a project",
        "description": "Changes the project name only. Keys rotate through `POST /projects/{id}/keys/regenerate`; the owner comes from the token.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "New name; trimmed, must not be empty.",
                    "minLength": 1
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteProject",
        "tags": [
          "Projects"
        ],
        "summary": "Delete a project and everything under it",
        "description": "Irreversible. Deletes plan-feature links, subscriptions, plans, gateways, features, customers, e-mail events and e-mail settings, then the project. Does not cancel anything at the payment provider.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deletion receipt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "object",
                      "properties": {
                        "gateways": {
                          "type": "integer"
                        },
                        "plans": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "gateways",
                        "plans"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/deletion-preview": {
      "get": {
        "operationId": "getProjectDeletionPreview",
        "tags": [
          "Projects"
        ],
        "summary": "What deleting a project would remove",
        "description": "Read-only counts of gateways, plans, features, subscriptions and active subscriptions (`active` or `trialing`) under the project.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gateways": {
                      "type": "integer"
                    },
                    "plans": {
                      "type": "integer"
                    },
                    "features": {
                      "type": "integer"
                    },
                    "subscriptions": {
                      "type": "integer"
                    },
                    "activeSubscriptions": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "gateways",
                    "plans",
                    "features",
                    "subscriptions",
                    "activeSubscriptions"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/keys/regenerate": {
      "post": {
        "operationId": "regenerateProjectKeys",
        "tags": [
          "Projects"
        ],
        "summary": "Rotate the project keys",
        "description": "Mints a new public key and a new secret key; the previous ones stop working immediately. The plaintext secret key is returned only here.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "New keys",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "publicKey": {
                      "type": "string",
                      "description": "`pk_…`"
                    },
                    "secretKey": {
                      "type": "string",
                      "description": "`psk_…` — shown once."
                    }
                  },
                  "required": [
                    "publicKey",
                    "secretKey"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/features": {
      "get": {
        "operationId": "listFeatures",
        "tags": [
          "Features"
        ],
        "summary": "List a project’s features",
        "description": "Paginated and searched on the server, ordered by slug. `products` (at the root) lists the distinct products the whole catalog is split into — empty for a catalog not split by product.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 50, ceiling 200; the reported `page.limit` is the applied value.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip. Default 0. Wins over `page` when both are sent.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Legacy 1-based page number, kept for compatibility. Ignored when `offset` is present.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Matches name, slug or description.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Only features of this product.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Features",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Feature"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/products": {
      "get": {
        "operationId": "listProducts",
        "tags": [
          "Features"
        ],
        "summary": "List the products of a project’s catalog",
        "description": "Distinct non-null `product` values of the project’s features, with a display label. Not paginated (`page.total` is `null`).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "product": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "product",
                          "label"
                        ]
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/dashboard/{id}": {
      "get": {
        "operationId": "getProjectDashboard",
        "tags": [
          "Projects"
        ],
        "summary": "Project summary and counts",
        "description": "The project (public key, secret-key prefix) plus the number of active gateways and the plans under them. `project.secretKey` is always empty — the secret is stored only as a hash.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dashboard",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DashboardMetrics"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/email-settings": {
      "get": {
        "operationId": "getEmailSettings",
        "tags": [
          "Emails"
        ],
        "summary": "Effective transactional e-mail settings",
        "description": "The effective branding and per-kind templates (stored values merged over defaults), plus `defaults` and the catalog of `kinds`.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Settings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/EmailSettings"
                    }
                  ],
                  "type": "object",
                  "properties": {
                    "defaults": {
                      "$ref": "#/components/schemas/EmailSettings"
                    },
                    "kinds": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "audience": {
                            "type": "string",
                            "enum": [
                              "subscriber",
                              "owner"
                            ]
                          },
                          "label": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "audience",
                          "label"
                        ]
                      }
                    }
                  },
                  "required": [
                    "defaults",
                    "kinds"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "operationId": "updateEmailSettings",
        "tags": [
          "Emails"
        ],
        "summary": "Patch e-mail settings",
        "description": "Partial update: send `brand` and/or `templates`. `templates.<kind> = null` restores that kind to the default. All validation errors are returned at once in `error.details.errors`. Returns the stored (not merged) settings.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailSettingsPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailSettingsPatch"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/emails/preview": {
      "post": {
        "operationId": "previewEmail",
        "tags": [
          "Emails"
        ],
        "summary": "Render an e-mail preview",
        "description": "Renders one e-mail kind with sample data using the same code that sends. Optional `settings` previews an unsaved draft (validated like a save). Sends nothing.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "$ref": "#/components/schemas/EmailKind"
                  },
                  "settings": {
                    "$ref": "#/components/schemas/EmailSettingsPatch"
                  }
                },
                "required": [
                  "kind"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rendered e-mail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "$ref": "#/components/schemas/EmailKind"
                    },
                    "subject": {
                      "type": "string"
                    },
                    "html": {
                      "type": "string"
                    },
                    "text": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "kind",
                    "subject",
                    "html",
                    "text"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/emails": {
      "get": {
        "operationId": "listEmailEvents",
        "tags": [
          "Emails"
        ],
        "summary": "E-mail send log",
        "description": "The project’s e-mail events, newest first, 25 per page (fixed; `limit`/`offset` are not read). Contains subscribers’ e-mail addresses.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by event status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "sent",
                "failed",
                "skipped"
              ]
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Filter by e-mail kind.",
            "schema": {
              "$ref": "#/components/schemas/EmailKind"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Legacy 1-based page number, kept for compatibility. Ignored when `offset` is present.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailEvent"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/features": {
      "post": {
        "operationId": "createFeature",
        "tags": [
          "Features"
        ],
        "summary": "Create a feature",
        "description": "Creates a feature (an access slug your app checks) in a project. `slug` defaults to the lower-cased name with spaces replaced by hyphens. Note: access and validation failures on this route surface as 500 with the underlying message (the handler does not translate them).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "id": {
                    "type": "string",
                    "description": "Optional id; a UUID is generated otherwise."
                  },
                  "slug": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "product": {
                    "type": "string",
                    "description": "Product of the suite this feature belongs to; omit for a catalog not split by product."
                  }
                },
                "required": [
                  "projectId",
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created feature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NewFeature"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/features/{id}": {
      "put": {
        "operationId": "updateFeature",
        "tags": [
          "Features"
        ],
        "summary": "Update a feature",
        "description": "Overwrites `name`, `slug`, `description` and `product` (fields omitted from the body are written as absent/null). Returns an empty object.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Feature id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "product": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteFeature",
        "tags": [
          "Features"
        ],
        "summary": "Delete a feature",
        "description": "Deletes the feature and its links to plans. Returns an empty object.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Feature id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/plans/{id}/features": {
      "get": {
        "operationId": "listPlanFeatures",
        "tags": [
          "Features"
        ],
        "summary": "Features linked to a plan",
        "description": "The features attached to a plan. Not paginated (`page.total` is `null`).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Plan id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Features",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "slug"
                        ]
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/plan-features": {
      "post": {
        "operationId": "setPlanFeatures",
        "tags": [
          "Features"
        ],
        "summary": "Replace a plan’s features",
        "description": "Replaces the full set of features linked to a plan with `featureIds` (an empty or absent list clears it). Access failures surface as 500 with the underlying message.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "planId": {
                    "type": "string"
                  },
                  "featureIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "planId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/gateways/{id}": {
      "get": {
        "operationId": "listGateways",
        "tags": [
          "Gateways"
        ],
        "summary": "List a project’s gateways",
        "description": "The path id is a PROJECT id. Credentials are never returned in full: `apiKeyPreview` (prefix + last 4), `mode` (`test`/`live`), `hasApiKey`, `hasWebhookSecret`, and `planCount` (zero is the only value that allows deletion). Not paginated.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Gateways",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Gateway"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteGateway",
        "tags": [
          "Gateways"
        ],
        "summary": "Delete a gateway",
        "description": "Refused with 400 while any plan is linked to the gateway. Returns an empty object.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/gateways": {
      "post": {
        "operationId": "saveGateway",
        "tags": [
          "Gateways"
        ],
        "summary": "Create or update a gateway",
        "description": "Upsert by `id`. Creating requires `apiKey`. On update, an absent `apiKey`/`webhookSecret` keeps the stored one. Credentials are encrypted at rest. Saving without a webhook secret triggers a one-time warning e-mail to the operator. Returns an empty object.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Existing gateway id to update; omit to create."
                  },
                  "projectId": {
                    "type": "string"
                  },
                  "gatewayName": {
                    "type": "string",
                    "description": "Provider: `stripe`, `mercadopago` or `hotmart` (only Stripe charges end to end)."
                  },
                  "name": {
                    "type": "string",
                    "description": "Nickname shown in the dashboard."
                  },
                  "apiKey": {
                    "type": "string",
                    "description": "Provider secret key (e.g. `sk_live_…`). Required on create."
                  },
                  "webhookSecret": {
                    "type": "string",
                    "description": "Provider webhook signing secret (e.g. `whsec_…`)."
                  },
                  "isActive": {
                    "type": "boolean",
                    "default": true
                  }
                },
                "required": [
                  "projectId",
                  "gatewayName"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/gateways/test": {
      "post": {
        "operationId": "testGatewayCredentials",
        "tags": [
          "Gateways"
        ],
        "summary": "Test provider credentials before saving",
        "description": "Calls the provider with a key that is not stored anywhere. A rejected key is a successful test: 200 with `ok: false` and the reason in `detail`.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "gatewayName": {
                    "type": "string"
                  },
                  "apiKey": {
                    "type": "string"
                  },
                  "webhookSecret": {
                    "type": "string"
                  }
                },
                "required": [
                  "projectId",
                  "gatewayName",
                  "apiKey"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test outcome",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectionTest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/gateways/{id}/test": {
      "post": {
        "operationId": "testGateway",
        "tags": [
          "Gateways"
        ],
        "summary": "Test a stored gateway",
        "description": "Checks whether the stored credential can talk to the provider. 200 even when the connection fails (`ok: false`, reason in `detail`).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test outcome",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectionTest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/gateways/{id}/webhooks": {
      "get": {
        "operationId": "inspectGatewayWebhooks",
        "tags": [
          "Gateways"
        ],
        "summary": "Inspect webhooks registered at the provider",
        "description": "Read-only diagnosis: lists the provider’s webhook endpoints and whether one is enabled at this worker’s `/webhook/{gatewayId}` URL (`match`). Providers without inspection answer 501 (`error.code` `INTERNAL_ERROR`).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookReport"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "501": {
            "description": "Provider does not support webhook inspection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/gateways/{id}/auto-setup": {
      "post": {
        "operationId": "autoSetupGatewayWebhook",
        "tags": [
          "Gateways"
        ],
        "summary": "Register the webhook at the provider",
        "description": "Deletes any existing endpoint for this worker’s URL, creates a new one subscribed to `checkout.session.completed`, `invoice.payment_succeeded`, `invoice.payment_failed`, `customer.subscription.updated`, `customer.subscription.deleted`, and stores the new signing secret (which changes).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Configured",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhookSecret": {
                      "type": "string",
                      "description": "The new provider signing secret."
                    }
                  },
                  "required": [
                    "webhookSecret"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/gateways/{id}/clone": {
      "post": {
        "operationId": "clonePlansIntoGateway",
        "tags": [
          "Gateways"
        ],
        "summary": "Copy plans from another gateway",
        "description": "Copies every plan (and its feature links) of `sourceGatewayId` into this gateway with new ids and `remotePriceId: null` (they must be re-synced). Both gateways must be accessible to the caller. Returns an empty object.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Target gateway id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourceGatewayId": {
                    "type": "string"
                  }
                },
                "required": [
                  "sourceGatewayId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cloned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/plans/{id}": {
      "get": {
        "operationId": "listPlans",
        "tags": [
          "Plans"
        ],
        "summary": "List a gateway’s plans",
        "description": "The path id is a GATEWAY id. Paginated, ordered by product, price and interval; each plan carries its features.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 50, ceiling 200; the reported `page.limit` is the applied value.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip. Default 0. Wins over `page` when both are sent.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Legacy 1-based page number, kept for compatibility. Ignored when `offset` is present.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Matches plan name, id or product.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Only plans of this product.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Plan"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deletePlan",
        "tags": [
          "Plans"
        ],
        "summary": "Delete a plan",
        "description": "Refused with 400 while any subscription references the plan. Does not delete the price at the provider. Returns an empty object.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Plan id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/plans": {
      "post": {
        "operationId": "createPlan",
        "tags": [
          "Plans"
        ],
        "summary": "Create or update a plan and sync it to the provider",
        "description": "Upserts the plan by `planId` and creates the price at the provider. Price is in CENTS. When `product` is set, the stored and provider name becomes `<Product> — <name>`. If the provider sync fails the plan is still saved locally and the response is 207 with `status: \"partial_success\"`. Note `price: 0` is rejected as missing.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectGatewayId": {
                    "type": "string"
                  },
                  "planId": {
                    "type": "string",
                    "description": "The plan id (chosen by the caller)."
                  },
                  "name": {
                    "type": "string"
                  },
                  "price": {
                    "type": "integer",
                    "description": "Price in cents (2999 = 29.99).",
                    "minimum": 1
                  },
                  "currency": {
                    "type": "string",
                    "default": "BRL"
                  },
                  "interval": {
                    "type": "string",
                    "description": "Billing interval, e.g. `month` or `year`.",
                    "default": "month"
                  },
                  "productId": {
                    "type": "string",
                    "description": "Tier identifier (e.g. starter/pro/scale)."
                  },
                  "product": {
                    "type": "string",
                    "description": "Which product of the catalog this plan sells."
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "projectGatewayId",
                  "planId",
                  "name",
                  "price"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved and synced",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "success"
                    },
                    "remoteId": {
                      "type": "string",
                      "description": "Provider price id."
                    },
                    "localPlan": {
                      "$ref": "#/components/schemas/PlanRecord"
                    }
                  },
                  "required": [
                    "status",
                    "remoteId",
                    "localPlan"
                  ]
                }
              }
            }
          },
          "207": {
            "description": "Saved locally; provider sync failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "partial_success"
                    },
                    "localPlan": {
                      "$ref": "#/components/schemas/PlanRecord"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status",
                    "localPlan",
                    "error"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/subscriptions/{id}": {
      "get": {
        "operationId": "listSubscriptions",
        "tags": [
          "Subscriptions"
        ],
        "summary": "List a project’s subscriptions",
        "description": "The path id is a PROJECT id. Ordered by `updatedAt` descending. `status=active` matches `active` and `trialing`; `all` or absent matches everything; any other value matches exactly.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "`all`, `active` (active + trialing) or an exact provider status such as `past_due` or `canceled`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Matches customer e-mail, userId, subscription id, plan product or plan name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 50, ceiling 200; the reported `page.limit` is the applied value.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip. Default 0. Wins over `page` when both are sent.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Legacy 1-based page number, kept for compatibility. Ignored when `offset` is present.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubscriptionListItem"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/admin/subscriptions/{id}": {
      "get": {
        "operationId": "getSubscription",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Look up one subscription by id",
        "description": "Finds a subscription by its id (the provider subscription id), then checks the caller owns its project.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Subscription id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionSummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/subscriptions/{id}/resend": {
      "post": {
        "operationId": "resendSubscriptionEmail",
        "tags": [
          "Subscriptions",
          "Emails"
        ],
        "summary": "Resend a subscription’s transactional e-mail",
        "description": "Rebuilds the subscription transition from the stored row and dispatches it through the same idempotent path as the webhook — an e-mail already sent is not sent again.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Subscription id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "Provider event type to replay.",
                    "default": "checkout.session.completed"
                  },
                  "invoiceId": {
                    "type": "string",
                    "description": "Invoice id, for receipt idempotency."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Processed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptionId": {
                      "type": "string"
                    },
                    "eventType": {
                      "type": "string"
                    },
                    "recipient": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "subscriptionId",
                    "eventType",
                    "recipient"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/subscription/{id}": {
      "put": {
        "operationId": "updateSubscriptionItems",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Change quantity or price of a provider subscription",
        "description": "The path id is a PROJECT id. Updates the items of `subscriptionId` at the provider (Stripe may invoice immediately). Uses the project’s gateway named by `gateway`.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Project id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscriptionId": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "integer"
                  },
                  "priceId": {
                    "type": "string",
                    "description": "Provider price id."
                  },
                  "gateway": {
                    "type": "string",
                    "default": "stripe"
                  }
                },
                "required": [
                  "subscriptionId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/checkout": {
      "post": {
        "operationId": "createCheckoutSession",
        "tags": [
          "Checkout"
        ],
        "summary": "Create a checkout session",
        "description": "Public — called from the end customer’s browser. Returns only the provider checkout URL. Refuses with 409 when the user already holds an active plan of the same product catalog (change plans through `POST /account` with `planId`).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "Your user id — the key that links subscriptions to your user."
                  },
                  "planId": {
                    "type": "string"
                  },
                  "success_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "cancel_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "userId",
                  "planId",
                  "success_url",
                  "cancel_url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedirectUrl"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Already subscribed in this catalog. `error.details` carries `subscriptionId` and `currentPlanId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/account": {
      "post": {
        "operationId": "createCustomerPortalSession",
        "tags": [
          "Checkout"
        ],
        "summary": "Open the customer portal, or a plan change",
        "description": "Public — called from the end customer’s browser. Without `planId`: returns a provider customer-portal URL (requires `email` or `userId`, `projectId` or `publicKey`, and `return_url`). With `planId`: returns a portal URL that confirms moving the user’s active subscription of that catalog to `planId` (requires `userId` and `return_url`); 409 when there is nothing to change, the plan is already held, or the plan is not synced.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "userId": {
                    "type": "string"
                  },
                  "projectId": {
                    "type": "string"
                  },
                  "publicKey": {
                    "type": "string",
                    "description": "Project public key (`pk_…`), alternative to `projectId`."
                  },
                  "gateway": {
                    "type": "string",
                    "default": "stripe"
                  },
                  "gatewayId": {
                    "type": "string"
                  },
                  "return_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "planId": {
                    "type": "string",
                    "description": "Target plan for a plan change."
                  }
                },
                "required": [
                  "return_url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Portal URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedirectUrl"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Plan change not possible.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/customers": {
      "put": {
        "operationId": "upsertCustomer",
        "tags": [
          "Checkout"
        ],
        "summary": "Create or update a customer at the provider",
        "description": "Server-to-server. Links your `userId` to a provider customer, updating it if one exists (locally or found remotely) or creating it otherwise.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          },
          {
            "projectSecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string"
                  },
                  "projectId": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "name": {
                    "type": "string"
                  },
                  "gateway": {
                    "type": "string",
                    "default": "stripe"
                  }
                },
                "required": [
                  "userId",
                  "projectId",
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated existing customer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "remoteCustomerId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "remoteCustomerId"
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Created and linked a new customer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "remoteCustomerId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "remoteCustomerId"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/sdk/v1/plans": {
      "get": {
        "operationId": "listPublicPlans",
        "tags": [
          "SDK"
        ],
        "summary": "Public plan catalog",
        "description": "Plans of the project’s active gateways, for a pricing table. Identified by the public key. `isLive` says whether the gateway key is a live one. Not paginated (`page.total` is `null`).",
        "security": [
          {
            "publicKey": []
          }
        ],
        "parameters": [
          {
            "name": "gatewayId",
            "in": "query",
            "required": false,
            "description": "Only plans of this gateway.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Only plans of this product.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "page"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PublicPlan"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/sdk/v1/permissions": {
      "get": {
        "operationId": "getUserPermissions",
        "tags": [
          "SDK"
        ],
        "summary": "A user’s subscriptions and unlocked features",
        "description": "Feature gate for one of your users. Identify the project with `Authorization: Bearer psk_…` (server) or `?publicKey=pk_…` (browser); when an `Authorization: Bearer` header is present the public key is ignored. A subscription counts as active only with an active status AND inside its paid period.",
        "security": [
          {
            "projectSecretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "description": "Your user id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Permissions"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/webhook/{id}": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "description": "Raw provider event, verified against the gateway’s stored webhook secret.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "receiveGatewayWebhook",
        "summary": "Provider webhook receiver",
        "description": "The URL you register at the payment provider (or let `POST /gateways/{id}/auto-setup` register). Called by the provider, not by your code. `id` is a gateway id (or, as a fallback, a project id — its first gateway is used). Signature failures answer 400.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id (preferred) or project id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stripe-signature",
            "in": "header",
            "required": false,
            "description": "Stripe signature header.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "required": false,
            "description": "Signature header used by other providers (read when `stripe-signature` is absent).",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/webhook/{gatewayName}/{id}": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "description": "Raw provider event, verified against the gateway’s stored webhook secret.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empty"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "receiveGatewayWebhookLegacy",
        "deprecated": true,
        "summary": "Provider webhook receiver (legacy form)",
        "description": "Legacy URL form. `id` is a gateway id, or a project id combined with `gatewayName`. Prefer `/webhook/{id}`.",
        "parameters": [
          {
            "name": "gatewayName",
            "in": "path",
            "required": true,
            "description": "Provider name, e.g. `stripe`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Gateway id, or project id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stripe-signature",
            "in": "header",
            "required": false,
            "description": "Stripe signature header.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "required": false,
            "description": "Signature header used by other providers (read when `stripe-signature` is absent).",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Infrastructure Auth session token (RS256, issuer `riligar-auth`) identifying a person; the `sub` claim is the project owner."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code with PKCE (S256), issued by Infrastructure Auth. Use it to act on behalf of a person; scopes are listed in the authorization server metadata.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://auth.worker.myinfrastructure.click/oauth/authorize",
            "tokenUrl": "https://auth.worker.myinfrastructure.click/oauth/token",
            "refreshUrl": "https://auth.worker.myinfrastructure.click/oauth/token",
            "scopes": {
              "payments:read": "Read projects, metrics, subscriptions and plans (MCP).",
              "payments:write": "Create plans and checkout links (MCP).",
              "payments:admin": "Change subscriptions (MCP)."
            }
          }
        }
      },
      "projectSecretKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "psk_<64 hex>",
        "description": "Project secret key, sent as `Authorization: Bearer psk_…`. Authorizes that one project and no other. Server-side only."
      },
      "publicKey": {
        "type": "apiKey",
        "in": "query",
        "name": "publicKey",
        "description": "Project public key `pk_…`. Identifies, does not authorize; ships in the browser bundle."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine code. On this API it is derived from the HTTP status (400 VALIDATION_ERROR, 401 UNAUTHORIZED, 402 PLAN_LIMIT, 403 FORBIDDEN, 404 NOT_FOUND, 409 CONFLICT, 5xx INTERNAL_ERROR).",
                "enum": [
                  "UNAUTHORIZED",
                  "FORBIDDEN",
                  "WRONG_AUDIENCE",
                  "INSUFFICIENT_SCOPE",
                  "NOT_FOUND",
                  "CONFLICT",
                  "NAME_TAKEN",
                  "EXPIRED",
                  "HAS_RESOURCES",
                  "VALIDATION_ERROR",
                  "UNPROCESSABLE",
                  "RATE_LIMITED",
                  "PAYLOAD_TOO_LARGE",
                  "PLAN_LIMIT",
                  "SUBSCRIPTION_REQUIRED",
                  "INTERNAL_ERROR",
                  "SERVICE_UNAVAILABLE",
                  "UPSTREAM_ERROR"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable (often Portuguese); may change."
              },
              "details": {
                "type": "object",
                "description": "Structured context, when any.",
                "additionalProperties": true
              },
              "retriable": {
                "type": "boolean",
                "description": "Present and true only for transient codes."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Page": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`null` when the route does not count."
          },
          "hasMore": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`null` when `total` is `null`."
          }
        },
        "required": [
          "limit",
          "offset",
          "total",
          "hasMore"
        ]
      },
      "Empty": {
        "type": "object",
        "description": "Empty object `{}`.",
        "additionalProperties": false
      },
      "RedirectUrl": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "url"
        ]
      },
      "ServiceStatus": {
        "type": "object",
        "properties": {
          "service": {
            "type": "string",
            "const": "payments"
          },
          "status": {
            "type": "string",
            "enum": [
              "operational",
              "degraded",
              "down"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "operational",
                    "degraded",
                    "down"
                  ]
                },
                "latencyMs": {
                  "type": "integer"
                },
                "metrics": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "number"
                  }
                },
                "error": {
                  "type": "string",
                  "const": "unavailable"
                }
              },
              "required": [
                "name",
                "description",
                "status",
                "latencyMs"
              ]
            }
          }
        },
        "required": [
          "service",
          "status",
          "checkedAt",
          "checks"
        ]
      },
      "PlanNotice": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "active",
              "free",
              "none",
              "unknown"
            ]
          },
          "plan": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Project limit; `null` when unlimited or unknown."
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "state",
          "plan",
          "limit",
          "message"
        ]
      },
      "AccountInventory": {
        "type": "object",
        "properties": {
          "projects": {
            "type": "integer"
          },
          "gateways": {
            "type": "integer"
          },
          "plans": {
            "type": "integer"
          },
          "subscriptions": {
            "type": "integer"
          },
          "activeSubscriptions": {
            "type": "integer"
          }
        },
        "required": [
          "projects",
          "gateways",
          "plans",
          "subscriptions",
          "activeSubscriptions"
        ]
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "publicKey": {
            "type": "string",
            "description": "`pk_…` — safe to ship in a browser bundle."
          },
          "secretKeyPrefix": {
            "type": [
              "string",
              "null"
            ],
            "description": "First 12 characters of the secret key."
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ]
          },
          "ownerEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "publicKey"
        ]
      },
      "CreatedProject": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "publicKey": {
            "type": "string"
          },
          "secretKey": {
            "type": "string",
            "description": "`psk_…` plaintext — returned only once."
          },
          "secretKeyPrefix": {
            "type": "string"
          },
          "ownerId": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "publicKey",
          "secretKey"
        ]
      },
      "ProjectOverview": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "publicKey": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "summary": {
            "type": "object",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "empty",
                  "test",
                  "live"
                ]
              },
              "label": {
                "type": "string"
              },
              "count": {
                "type": "integer"
              }
            },
            "required": [
              "state",
              "label",
              "count"
            ]
          },
          "planCount": {
            "type": "integer"
          },
          "unsyncedPlans": {
            "type": "integer",
            "description": "Plans without a provider price (cannot be subscribed)."
          },
          "featureCount": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "summary",
          "planCount",
          "unsyncedPlans",
          "featureCount"
        ]
      },
      "DashboardMetrics": {
        "type": "object",
        "properties": {
          "project": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "publicKey": {
                "type": "string"
              },
              "secretKey": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Always empty."
              },
              "secretKeyPrefix": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "name",
              "publicKey"
            ]
          },
          "metrics": {
            "type": "object",
            "properties": {
              "activeGateways": {
                "type": "integer"
              },
              "totalPlans": {
                "type": "integer"
              }
            },
            "required": [
              "activeGateways",
              "totalPlans"
            ]
          }
        },
        "required": [
          "project",
          "metrics"
        ]
      },
      "Feature": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "product": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "projectId",
          "name",
          "slug"
        ]
      },
      "NewFeature": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "product": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "projectId",
          "name",
          "slug"
        ]
      },
      "Gateway": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "gatewayName": {
            "type": "string",
            "description": "`stripe`, `mercadopago` or `hotmart`."
          },
          "isActive": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "apiKeyPreview": {
            "type": [
              "string",
              "null"
            ],
            "description": "e.g. `sk_live_…4242`."
          },
          "mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "test",
              "live",
              null
            ]
          },
          "hasApiKey": {
            "type": "boolean"
          },
          "hasWebhookSecret": {
            "type": "boolean"
          },
          "planCount": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "projectId",
          "gatewayName",
          "hasApiKey",
          "hasWebhookSecret",
          "planCount"
        ]
      },
      "ConnectionTest": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "test",
              "live",
              null
            ]
          },
          "account": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider account display name."
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the connection failed."
          },
          "gatewayName": {
            "type": "string"
          },
          "elapsed": {
            "type": "integer",
            "description": "Milliseconds."
          },
          "hasWebhookSecret": {
            "type": "boolean"
          }
        },
        "required": [
          "ok",
          "gatewayName",
          "elapsed",
          "hasWebhookSecret"
        ]
      },
      "WebhookReport": {
        "type": "object",
        "properties": {
          "expectedUrl": {
            "type": "string",
            "format": "uri"
          },
          "match": {
            "type": "boolean",
            "description": "An enabled endpoint points at `expectedUrl`."
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "events": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "matchesExpected": {
                  "type": "boolean"
                }
              },
              "required": [
                "id",
                "url",
                "status"
              ]
            }
          },
          "gatewayName": {
            "type": "string"
          },
          "hasWebhookSecret": {
            "type": "boolean"
          }
        },
        "required": [
          "expectedUrl",
          "match",
          "endpoints",
          "gatewayName",
          "hasWebhookSecret"
        ]
      },
      "PlanRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectGatewayId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "productId": {
            "type": [
              "string",
              "null"
            ]
          },
          "product": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "integer",
            "description": "Cents."
          },
          "currency": {
            "type": "string"
          },
          "interval": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "remotePriceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider price id; `null` = not synced, cannot be subscribed."
          }
        },
        "required": [
          "id",
          "projectGatewayId",
          "name",
          "price"
        ]
      },
      "Plan": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PlanRecord"
          }
        ],
        "type": "object",
        "properties": {
          "features": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "planId": {
                  "type": "string"
                },
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "value": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "slug"
              ]
            }
          }
        },
        "required": [
          "features"
        ]
      },
      "PublicPlan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "productId": {
            "type": [
              "string",
              "null"
            ]
          },
          "product": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "integer",
            "description": "Cents."
          },
          "currency": {
            "type": "string"
          },
          "interval": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "remotePriceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "gatewayName": {
            "type": "string"
          },
          "projectGatewayId": {
            "type": "string"
          },
          "isLive": {
            "type": "boolean",
            "description": "The gateway uses a live (charging) key."
          },
          "features": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FeatureGrant"
            }
          }
        },
        "required": [
          "id",
          "name",
          "price",
          "gatewayName",
          "projectGatewayId",
          "isLive",
          "features"
        ]
      },
      "FeatureGrant": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "value": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "slug"
        ]
      },
      "Permissions": {
        "type": "object",
        "properties": {
          "hasActiveSubscription": {
            "type": "boolean"
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "subscriptionId": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "gateway": {
                  "type": "string"
                },
                "expiresAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "planName": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "planId": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "subscriptionId",
                "status"
              ]
            }
          },
          "features": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FeatureGrant"
            }
          }
        },
        "required": [
          "hasActiveSubscription",
          "subscriptions",
          "features"
        ]
      },
      "SubscriptionListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "userId": {
            "type": "string"
          },
          "customerEmail": {
            "type": "string",
            "description": "Customer e-mail, or the userId when no e-mail is stored."
          },
          "status": {
            "type": "string",
            "description": "Provider status, e.g. `active`, `trialing`, `past_due`, `canceled`."
          },
          "gatewayName": {
            "type": "string"
          },
          "gatewayNickname": {
            "type": [
              "string",
              "null"
            ]
          },
          "planName": {
            "type": [
              "string",
              "null"
            ]
          },
          "planProduct": {
            "type": [
              "string",
              "null"
            ]
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "userId",
          "status",
          "gatewayName"
        ]
      },
      "SubscriptionSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "userId": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "planId": {
            "type": "string"
          },
          "customerEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "gatewayName": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "projectId",
          "userId",
          "status",
          "planId",
          "gatewayName"
        ]
      },
      "EmailKind": {
        "type": "string",
        "enum": [
          "welcome",
          "receipt",
          "payment_failed",
          "deactivated",
          "reactivated",
          "expiring",
          "first_sale",
          "owner_failed",
          "owner_canceled",
          "gateway_no_webhook",
          "keys_regenerated"
        ]
      },
      "EmailTemplate": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "subject": {
            "type": "string",
            "description": "No line breaks; max 200 chars.",
            "maxLength": 200
          },
          "title": {
            "type": "string",
            "maxLength": 120
          },
          "body": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 2000
            },
            "description": "Paragraphs (plain text, no HTML)."
          },
          "cta": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "maxLength": 60
              },
              "target": {
                "type": "string",
                "enum": [
                  "portal",
                  "checkout",
                  "support",
                  "none"
                ]
              }
            }
          }
        }
      },
      "EmailBrand": {
        "type": "object",
        "properties": {
          "systemName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "https URL."
          },
          "accentColor": {
            "type": [
              "string",
              "null"
            ],
            "description": "6-digit hex, e.g. `#11181C`."
          },
          "fromName": {
            "type": [
              "string",
              "null"
            ]
          },
          "replyTo": {
            "type": [
              "string",
              "null"
            ],
            "description": "E-mail address."
          },
          "supportUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "https URL."
          },
          "footerNote": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          }
        }
      },
      "EmailSettings": {
        "type": "object",
        "properties": {
          "brand": {
            "$ref": "#/components/schemas/EmailBrand"
          },
          "templates": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/EmailTemplate"
            }
          }
        },
        "required": [
          "brand",
          "templates"
        ]
      },
      "EmailSettingsPatch": {
        "type": "object",
        "properties": {
          "brand": {
            "$ref": "#/components/schemas/EmailBrand"
          },
          "templates": {
            "type": "object",
            "description": "Keyed by e-mail kind; `null` restores the default.",
            "additionalProperties": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/EmailTemplate"
                },
                {
                  "type": "null"
                }
              ]
            }
          }
        }
      },
      "EmailEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "dedupeKey": {
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/EmailKind"
          },
          "audience": {
            "type": "string",
            "enum": [
              "subscriber",
              "owner"
            ]
          },
          "recipient": {
            "type": "string"
          },
          "subscriptionId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sent",
              "failed",
              "skipped"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "sentAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds."
          }
        },
        "required": [
          "id",
          "projectId",
          "kind",
          "audience",
          "recipient",
          "status",
          "attempts",
          "createdAt"
        ]
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid or missing input (`VALIDATION_ERROR`), or an operation refused by a business rule.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid or expired credential (`UNAUTHORIZED`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The credential cannot act here (`FORBIDDEN`): a project key used on another project, or on a route that requires a person’s token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found, or not yours (`NOT_FOUND`) — the two are indistinguishable on purpose.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected failure (`INTERNAL_ERROR`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
