{
  "openapi": "3.0.3",
  "info": {
    "title": "Rekey API",
    "description": "Self-hostable authentication + billing + admin REST API. All money is in integer minor units (cents). Every error carries a `code`, human `message`, and a `fix`.\n\n## Which credential do I send?\n\nSix different credentials guard this API. Each route documents its own — read the padlock, not this list. In short:\n\n- **Publishable key** (`rp_pub_…`, `Authorization: Bearer`) — the browser credential. Safe to ship in client-side JavaScript. Accepted on the public-bootstrap surface (sign-in/up, magic link, passkeys, OAuth start, plan/subscription reads, checkout, organizations). Restricted by the Application's origin allowlist rather than kept secret.\n- **Application secret key** (`rp_live_…` / `rp_test_…`, `Authorization: Bearer`) — the server-side credential. Accepted everywhere the publishable key is, plus the privileged surface it cannot reach (usage, credits, licenses, coupon redemption, session/user administration). Never ship it to a browser.\n- **End-user JWT** (`X-Rekey-User-Token`) — sent *in addition to* one of the two keys above on routes that act on behalf of a signed-in end user. You get it from the `token` in a sign-in response.\n- **Operator session** (`Authorization: Bearer` access token) — for the panel/operator surface under `/api/v1/tenant/*`.\n- **Operator PAT** (`rp_op_…`, `Authorization: Bearer`) — long-lived operator token for scripts and agents.\n- **Super-admin key** (`SUPER_ADMIN_KEY`, `Authorization: Bearer`) — the self-host bootstrap credential, for `/api/v1/admin/*` only.\n\nProvider webhook ingress routes take **no** credential at all — the provider signature is the authentication.",
    "version": "1.1.1"
  },
  "components": {
    "securitySchemes": {
      "superAdminKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "**What:** the self-host bootstrap admin credential — a single shared secret, not tied to any workspace or Application.\n\n**Where from:** the `SUPER_ADMIN_KEY` environment variable you set on the API deployment.\n\n**Where used:** `/api/v1/admin/*` only. Server-side only — this key can read and write every tenant on the deployment, so treat it like a root password. `requireApiKey` and the operator guards all reject it."
      },
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rp_live_… / rp_test_…",
        "description": "**What:** an Application-scoped **secret** key. Full server-side authority over that one Application. The `rp_test_` / `rp_live_` prefix follows the Application's `environment` and is descriptive only — nothing branches on it. Isolation is the Application boundary.\n\n**Where from:** Panel → Application → API Keys (shown once at mint time). Used by `@rekey.dev/node`.\n\n**Where used:** every non-admin, non-operator route. Some keys are minted with narrow scopes (`auth:read`, `auth:write`, `billing:read`, `billing:write`, `webhooks:read`); when a route needs a specific scope its description says so. May also be restricted by the Application's IP allowlist.\n\n**Never** put this in browser or mobile-client code — use the publishable key there."
      },
      "publishableKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rp_pub_…",
        "description": "**What:** the Application **publishable** key — a browser-safe credential. It identifies the Application and asserts \"legitimate public client\"; it grants nothing on its own (sign-in still needs the password or passkey, license verify still needs the license key).\n\n**Safe in browser code.** It is designed to be embedded in client-side JavaScript, mobile apps, and public HTML. Its protection is the Application's **origin allowlist** (Panel → Application → Access) plus per-route rate limits, not secrecy.\n\n**Where from:** Panel → Application (displayed alongside the secret key). Rotating it leaves the previous key valid for a grace window so clients can redeploy.\n\n**Where used:** only the public-bootstrap surface — routes guarded by `requirePublishableOrSecretKey`. A `requireApiKey` route rejects it with 401 `API_KEY_INVALID`; operator and admin routes use their own credentials and their own codes. API-key scopes do not apply to it — route membership is the gate. Where it does reach self-service billing or account management, the end-user's own token is required alongside it and is what authorizes the call; it never reaches administrative writes or another user's data."
      },
      "userToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Rekey-User-Token",
        "description": "**What:** the end-user access JWT — proof that a specific end user of your Application is signed in. It is a *second* credential: send it **together with** the publishable or secret key in `Authorization`, never instead of one.\n\n**Where from:** the `token` field returned by sign-in, sign-up, magic-link/OAuth/passkey completion, or `POST /api/v1/auth/refresh`.\n\n**Where used:** every route that acts on behalf of the signed-in user — `/api/v1/users/me`, subscriptions and payment methods, organizations, MFA enrollment, coupon redemption. The JWT carries its issuing `applicationId` and must match the Application the key resolved to, so a token from one Application can't be replayed against another."
      },
      "tenantSession": {
        "type": "http",
        "scheme": "bearer",
        "description": "**What:** an **operator** (workspace-member) session access token — the credential the Rekey panel uses. Scoped to one workspace plus the operator's live role in it (OWNER / ADMIN / MEMBER).\n\n**Where from:** `POST /api/v1/tenant/auth/sign-in` (or the passkey / OAuth equivalents) returns `accessToken`; refresh it at `POST /api/v1/tenant/auth/refresh`. Short-lived — for scripts and agents prefer an operator PAT.\n\n**Where used:** the operator surface under `/api/v1/tenant/*`. Workspace membership and role are re-read from the database on every request, so a downgrade or removal takes effect immediately. Routes that additionally demand OWNER/ADMIN, or a per-Application grant, say so in their description — OpenAPI cannot express that here."
      },
      "operatorPat": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rp_op_…",
        "description": "**What:** an operator **personal access token** — a long-lived stand-in for an operator session, for scripts, CI, and AI agents. Bound to one operator and one workspace, and carries its own scope list (`read`, `keys:mint`, `mcp:operator:write`, …) that is default-deny.\n\n**Where from:** `POST /api/v1/tenant/auth/api-tokens` (requires an operator session). Shown once at mint time.\n\n**Where used:** `/api/v1/tenant/operator/*` and the operator MCP endpoint. Workspace membership and the operator's live role are re-checked on every request, so revoking membership kills the token. Server-side only."
      },
      "mcpAccessToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "**What:** the credential for the operator MCP JSON-RPC endpoint (`/api/v1/tenant/mcp`). The guard accepts **either** an operator PAT (`rp_op_…`, which must carry the `read` scope) **or** an OAuth-issued access JWT (`typ: op_mcp_access`) minted by this deployment's operator-MCP authorization server. One bearer at a time — credentials are not chained.\n\n**Where from:** mint a PAT at `POST /api/v1/tenant/auth/api-tokens`, or let your MCP client run the OAuth flow starting at `GET /api/v1/tenant/mcp/oauth/authorize`.\n\n**Where used:** the MCP endpoint only. Read tools need nothing beyond authentication; write tools additionally require the `mcp:operator:write` scope (plus `applications:write` on the OAuth path) and an OWNER/ADMIN role floor."
      },
      "endUserMcpToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "**What:** an **end-user** MCP access JWT (`typ: mcp_access`), scoped to one Application and one of its end users. Distinct from `mcpAccessToken`, which is the operator-facing MCP credential — do not mix them up.\n\n**Where from:** the OAuth 2.1 + PKCE flow this deployment hosts per Application at `/api/v1/mcp/{slug}/oauth/authorize` → `/oauth/token`. An MCP client (e.g. a custom connector) drives it; the end user signs in and consents.\n\n**Where used:** `POST /api/v1/mcp/{slug}` only. Read-only — it exposes that user's own profile, subscription, and usage. Invalidated by the Application's session kill-switch (`tokenGeneration`) like any other end-user token."
      }
    },
    "schemas": {}
  },
  "paths": {
    "/health/live": {
      "get": {
        "summary": "Liveness probe — is the process up?",
        "tags": [
          "Health"
        ],
        "description": "Returns 200 as soon as the process can serve HTTP. Touches no dependencies, so it stays green through a database or Redis outage. Unauthenticated and exempt from rate limiting so an orchestrator can always reach it.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/health/ready": {
      "get": {
        "summary": "Readiness probe — can the process serve traffic?",
        "tags": [
          "Health"
        ],
        "description": "503 `not_ready` when Postgres is unreachable or a configured Redis is down, 200 `ready` otherwise. Unauthenticated and exempt from rate limiting.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/health": {
      "get": {
        "summary": "Aggregate health, with per-dependency detail",
        "tags": [
          "Health"
        ],
        "description": "200 `ok` or 503 `degraded`, plus `db` and `redis` fields so an operator can see which half is sick. Unauthenticated and exempt from rate limiting.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/.well-known/jwks.json": {
      "get": {
        "summary": "JSON Web Key Set (RS256 public keys for end-user access tokens)",
        "tags": [
          "Public · Auth"
        ],
        "description": "Public RSA keys (RFC 7517) for verifying RS256-signed end-user access tokens offline — no API call, no secret. Tokens carry the matching `kid` in their JWT header. Only Applications with `authConfig.tokenAlg = \"RS256\"` mint such tokens; the default HS256 tokens cannot be verified against this set. No authentication; responses are cacheable for 5 minutes.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kty": {
                            "type": "string",
                            "enum": [
                              "RSA"
                            ]
                          },
                          "kid": {
                            "type": "string"
                          },
                          "alg": {
                            "type": "string",
                            "enum": [
                              "RS256"
                            ]
                          },
                          "use": {
                            "type": "string",
                            "enum": [
                              "sig"
                            ]
                          },
                          "n": {
                            "type": "string"
                          },
                          "e": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "kty",
                          "kid",
                          "alg",
                          "use",
                          "n",
                          "e"
                        ]
                      }
                    }
                  },
                  "required": [
                    "keys"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "summary": "Inspect the Application this credential resolves to",
        "tags": [
          "Public · Me"
        ],
        "description": "Use this as the SDK smoke test — if it returns 200, your secret key is good.\n\nRequires an Application **secret** key with the `auth:read` scope; the publishable key is rejected. Returns the whole `authConfig` and `billingConfig` objects, not a filtered view — safe for the secret-key holder, but do not forward this response to a browser assuming it has been redacted. Provider, OAuth and email credentials live in separate encrypted columns and are never included.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/sign-up": {
      "post": {
        "summary": "Create an end-user via email + password",
        "tags": [
          "Public · Auth"
        ],
        "description": "Creates a new EndUser in the calling Application and issues a JWT. Email is unique per Application, not globally. Unless the Application turns `authConfig.sendVerificationEmailOnSignUp` off, the verification link goes out alongside the welcome mail — both are best-effort and neither can fail the sign-up. With `authConfig.requireEmailVerification` on this returns 403 `EMAIL_NOT_VERIFIED` instead of a session: the account IS created and the link IS sent (that switch overrides the send setting), but no token is issued until the address is confirmed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Free-form per-app metadata (display name, avatar, custom fields)."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/sign-in": {
      "post": {
        "summary": "Authenticate an existing end-user with email + password",
        "tags": [
          "Public · Auth"
        ],
        "description": "Verifies the password and issues a JWT. Returns 401 INVALID_CREDENTIALS for any auth failure (wrong email, wrong password, or sign-up via different method) — we never disclose which. When the Application sets `authConfig.requireEmailVerification`, a correct password on an unconfirmed address returns 403 EMAIL_NOT_VERIFIED instead, so your app can prompt the user to check their inbox rather than re-enter a password that is already right.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/mfa-verify": {
      "post": {
        "summary": "Exchange an MFA challenge token + TOTP/backup code for a session",
        "tags": [
          "Public · Auth"
        ],
        "description": "Used after /sign-in or OAuth callback returns `mfaRequired: true`. The challenge token is short-lived (5 minutes) and bound to (endUser, application). On success returns the same shape as /sign-in when MFA is not required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mfaChallengeToken",
                  "code"
                ],
                "properties": {
                  "mfaChallengeToken": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048
                  },
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/refresh": {
      "post": {
        "summary": "Exchange a refresh token for a new {access, refresh} pair",
        "tags": [
          "Public · Auth"
        ],
        "description": "The presented refresh token is revoked atomically with issuing the replacement. A presented-but-revoked token is treated as a replay and rejected with REFRESH_TOKEN_REUSED.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/sign-out": {
      "post": {
        "summary": "Revoke a refresh token",
        "tags": [
          "Public · Auth"
        ],
        "description": "Idempotent. Returns 200 even for unknown tokens — we don't disclose whether the token existed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/forgot-password": {
      "post": {
        "summary": "Request a password-reset token for an email",
        "tags": [
          "Public · Auth"
        ],
        "description": "Always returns 200 with `{ delivered: boolean, emailSent: boolean, resetToken: string|null }`. Never discloses whether the email exists. When the Application has email transport configured (BYO Resend or RESEND_DEFAULT_*), the email is sent and `resetToken` is null. Otherwise the legacy contract applies — caller forwards `resetToken` via their own provider.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "resetUrl": {
                    "type": "string",
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/magic-link/request": {
      "post": {
        "summary": "Request a magic-link sign-in email for an address",
        "tags": [
          "Public · Auth"
        ],
        "description": "Enumeration-safe: returns the same shape whether the email exists or not. When the Application has email transport configured, the link is sent and `magicLinkToken` is null. Otherwise the raw token is returned for the caller to forward via their own provider. Honours `authConfig.signupEnabled` — when disabled, magic links for new emails are silently refused (same enumeration-safe shape).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "signInUrl": {
                    "type": "string",
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/magic-link/verify": {
      "post": {
        "summary": "Consume a magic-link token + complete sign-in",
        "tags": [
          "Public · Auth"
        ],
        "description": "Single-use, 15-minute lifetime. Returns the same `SignInOutcome` shape as /sign-in — MFA-enrolled users get a challenge token; otherwise a full session. For tokens issued when the email had no account yet, the EndUser is created atomically with the consume (sign-up must be enabled on the Application).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/passkey/authenticate/start": {
      "post": {
        "summary": "Begin a passkey authentication ceremony",
        "tags": [
          "Public · Auth"
        ],
        "description": "Returns `{ options, expectedChallenge }`. Forward `options` to the browser (`navigator.credentials.get(...)`), persist `expectedChallenge` server-side in the customer app session (cookie / Redis), and send both back to /passkey/authenticate/complete. Usernameless when `email` is omitted; email-first when supplied (allowCredentials scoped to that user).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/passkey/authenticate/complete": {
      "post": {
        "summary": "Complete a passkey authentication and mint a session",
        "tags": [
          "Public · Auth"
        ],
        "description": "Verifies the browser response against `expectedChallenge` from /start. On success returns the same `SignInOutcome` shape as /sign-in — passkeys bypass MFA challenge (the passkey itself is a strong factor).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "response",
                  "expectedChallenge"
                ],
                "properties": {
                  "response": {
                    "type": "object"
                  },
                  "expectedChallenge": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/verify-email": {
      "post": {
        "summary": "Consume an email-verification token; marks `emailVerified: true`",
        "tags": [
          "Public · Auth"
        ],
        "description": "Single-use token, 24-hour lifetime. Refuses if the email on the token differs from the current account email (e.g. user changed email after the token was issued).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/resend-verification": {
      "post": {
        "summary": "Re-send an email-verification link to an address (no session required)",
        "tags": [
          "Public · Auth"
        ],
        "description": "For a user blocked by `authConfig.requireEmailVerification`, who by definition has no session and so cannot call /auth/send-verification.\n\nAlways returns 200 with `{ emailSent: boolean, verificationToken: string|null }` and never discloses whether the address exists, is already verified, or was delivered to — a **publishable**-key caller gets one constant body whatever happened. A secret-key caller gets the real outcome, and the raw token when the Application has no email transport configured (the same contract /forgot-password uses).\n\nNothing is sent when no verification link can be built — pass `verifyUrl`, or set the Application URL (Panel → Application → Auth). An already-verified address is a no-op rather than an error.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "verifyUrl": {
                    "type": "string",
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/reset-password": {
      "post": {
        "summary": "Consume a reset token + set a new password",
        "tags": [
          "Public · Auth"
        ],
        "description": "Single-use token; consumed atomically. On success, every refresh token for this end-user is revoked — anyone holding a session via the compromised credential is signed out.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "newPassword"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/change-password": {
      "post": {
        "summary": "Change the current user's password",
        "tags": [
          "Public · Auth"
        ],
        "description": "Requires the current password. On success, revokes every refresh token for this user — other devices are signed out. The caller's access token stays valid until its 15-min expiry.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currentPassword",
                  "newPassword"
                ],
                "properties": {
                  "currentPassword": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/send-verification": {
      "post": {
        "summary": "Send (or re-send) an email-verification link to the current user",
        "tags": [
          "Public · Auth"
        ],
        "description": "Mints a 24-hour single-use verification token bound to (user, application, email). If email transport is configured (BYO Resend or RESEND_DEFAULT_*), we send the email and `verificationToken` is null. Otherwise we return the raw token for the caller to forward via their own provider.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "verifyUrl": {
                    "type": "string",
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/passkey/register/start": {
      "post": {
        "summary": "Begin a passkey registration ceremony for the current user",
        "tags": [
          "Public · Auth"
        ],
        "description": "Returns `{ options, expectedChallenge }`. Forward `options` to `navigator.credentials.create(...)` and POST the result back to /passkey/register/complete along with the same `expectedChallenge`.\n\nA **publishable**-key caller must also send a step-up proof: `password` (the account password) or `code` (a current authenticator or unused backup code). A passkey bypasses the MFA challenge at sign-in, so a stolen access token alone must not be able to enroll one. Secret-key callers are not required to step up — the customer backend is the gate.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "password": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/passkey/register/complete": {
      "post": {
        "summary": "Complete a passkey registration; stores the credential",
        "tags": [
          "Public · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "response",
                  "expectedChallenge"
                ],
                "properties": {
                  "response": {
                    "type": "object"
                  },
                  "expectedChallenge": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  },
                  "deviceName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/passkeys": {
      "get": {
        "summary": "List the current user's registered passkeys",
        "tags": [
          "Public · Auth"
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/passkeys/{id}": {
      "delete": {
        "summary": "Remove a passkey from the current user",
        "tags": [
          "Public · Auth"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/sessions": {
      "get": {
        "summary": "List the current user's active sessions (live refresh tokens)",
        "tags": [
          "Public · Auth"
        ],
        "description": "Returns active sessions ordered newest-first, with the User-Agent + IP captured at issue time. Use the returned `id` to revoke individual sessions via DELETE /sessions/:id.",
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/sessions/{id}": {
      "delete": {
        "summary": "Revoke one session by id. Idempotent.",
        "tags": [
          "Public · Auth"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/sign-out-everywhere": {
      "post": {
        "summary": "Revoke every refresh token for the current user",
        "tags": [
          "Public · Auth"
        ],
        "description": "Used for \"sign out of all devices\" / suspected compromise. The caller's access token stays valid until its 15-min expiry; clear it client-side for full logout.",
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "summary": "Get the current end-user (from the user token alone)",
        "tags": [
          "Public · Auth"
        ],
        "description": "Resolves the end-user from the X-Rekey-User-Token JWT only — no Application secret key required. Intended for browser/client SDKs that hold only the user access token. Returns 401 USER_TOKEN_INVALID when the token is missing, expired, or malformed.",
        "security": [
          {
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/oauth/{provider}/start": {
      "post": {
        "summary": "Get the authorization URL for an OAuth provider",
        "tags": [
          "Public · OAuth"
        ],
        "description": "Returns the URL the browser should be redirected to. Pass an unguessable `state` to round-trip; verify it on callback before calling /callback here.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "state"
                ],
                "properties": {
                  "state": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/oauth/{provider}/callback": {
      "post": {
        "summary": "Exchange an OAuth code for a Rekey session",
        "tags": [
          "Public · OAuth"
        ],
        "description": "Pass the `code` query param from the provider callback. Returns a fresh access+refresh pair for the matched-or-created EndUser. Verify state CSRF *before* calling.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4096
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/oauth/identities": {
      "get": {
        "summary": "List OAuth providers linked to the current user",
        "tags": [
          "Public · OAuth"
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/oauth/{provider}/link/start": {
      "post": {
        "summary": "Begin an OAuth link flow for the current user",
        "tags": [
          "Public · OAuth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "state"
                ],
                "properties": {
                  "state": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/oauth/{provider}/link/complete": {
      "post": {
        "summary": "Complete an OAuth link — attaches the provider identity to the current user",
        "tags": [
          "Public · OAuth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4096
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/oauth/{provider}": {
      "delete": {
        "summary": "Remove an OAuth provider from the current user. Refuses if it would lock the account out.",
        "tags": [
          "Public · OAuth"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me": {
      "get": {
        "summary": "Get the current end-user (resolved from the user JWT)",
        "tags": [
          "Public · Auth"
        ],
        "description": "Requires an Application key (publishable or secret, Authorization header) AND the user JWT (X-Rekey-User-Token header). Callable from a browser with the publishable key, since the JWT is the authorizer and the response is that user's own record. Refuses to return data if the JWT was issued by a different Application than the key represents. Note `GET /api/v1/me` is different: it returns the Application, including its whole authConfig and billingConfig, so it stays secret-key-only.",
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Update the current end-user's own record",
        "tags": [
          "Public · Auth"
        ],
        "description": "Updates the EndUser identified by the X-Rekey-User-Token JWT. The token IS the authorizer, so this can only ever write the caller's own row — there is no id in the path and the write is scoped to (token subject, key's Application). **MERGE SEMANTICS (read this):** `metadata` is merged SHALLOWLY at the top level. A key you omit is left untouched; a key you send REPLACES that top-level key wholesale (nested objects are not deep-merged); a key sent as `null` is DELETED; and `\"metadata\": null` clears the whole object. Omitting `metadata` entirely changes nothing. Fields other than `metadata` are rejected with 400 — email, role, password and erasure state are not self-service and never will be on this route. One key INSIDE `metadata` is likewise refused (400 `METADATA_KEY_RESERVED`): `oidc`, which holds the OpenID Connect identity claims the Application asserts about this user to relying parties. Those are the operator's to write. Every other key, including ones that share a claim's name, stays yours.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "metadata": {
                    "description": "Free-form per-app metadata. Shallow-merged over what is stored; a top-level key set to null is deleted; null for the whole field clears it. Capped at 16KB serialized after the merge."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations": {
      "post": {
        "summary": "Create an organization; caller becomes the OWNER",
        "tags": [
          "Public · Organizations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "slug"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "metadata": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "List the caller's organizations (with their role in each)",
        "tags": [
          "Public · Organizations"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}": {
      "get": {
        "summary": "Fetch one organization the caller belongs to",
        "tags": [
          "Public · Organizations"
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Update org name / metadata (OWNER + ADMIN)",
        "tags": [
          "Public · Organizations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "metadata": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}/members": {
      "get": {
        "summary": "List members of an org the caller belongs to",
        "tags": [
          "Public · Organizations"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}/invitations": {
      "post": {
        "summary": "Invite a user (OWNER + ADMIN). Returns the raw token once.",
        "tags": [
          "Public · Organizations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "role"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "OWNER",
                      "ADMIN",
                      "MEMBER"
                    ]
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}/invitations/{invId}/revoke": {
      "post": {
        "summary": "Revoke a pending invitation (OWNER + ADMIN)",
        "tags": [
          "Public · Organizations"
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "invId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}/members/{euid}": {
      "patch": {
        "summary": "Change a member's role (OWNER manages anyone; ADMIN manages MEMBER + ADMIN)",
        "tags": [
          "Public · Organizations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "role"
                ],
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "OWNER",
                      "ADMIN",
                      "MEMBER"
                    ]
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Remove a member (or self). Refuses removing the last OWNER.",
        "tags": [
          "Public · Organizations"
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}/leave": {
      "post": {
        "summary": "Self-leave. OWNERs cannot leave (billing is tied to them) — transfer ownership first.",
        "tags": [
          "Public · Organizations"
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/{id}/switch": {
      "post": {
        "summary": "Make an org the session's active org; re-mints the token pair with the `oid` claim",
        "tags": [
          "Public · Organizations"
        ],
        "description": "Member-only. Returns a fresh {accessToken, refreshToken} pair carrying the active org. Read endpoints (e.g. GET /billing/entitlements) then default to this org's view + pool without an explicit `organizationId`. The active org survives refresh until you switch again, clear it, or leave the org.",
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/users/me/organizations/clear-active-organization": {
      "post": {
        "summary": "Clear the active org (back to the personal pool); re-mints the token pair",
        "tags": [
          "Public · Organizations"
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/organizations/accept-invitation": {
      "post": {
        "summary": "Accept an organization invitation. Creates the membership.",
        "tags": [
          "Public · Organizations"
        ],
        "description": "Callable from a browser with the publishable key plus the invitee's own end-user token. Creating an invitation already accepted the publishable key, so requiring a secret key here left the flow dead-ended: a portal could invite someone but never let them accept.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/plans": {
      "get": {
        "summary": "List active plans for the calling application",
        "tags": [
          "Public · Billing"
        ],
        "description": "Returns the public plan catalogue. End-users typically reach this via your pricing page.",
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/entitlements": {
      "get": {
        "summary": "Resolve the calling end-user's current entitlements",
        "tags": [
          "Public · Billing"
        ],
        "description": "Union of the benefits granted by the user's ACTIVE subscriptions: feature flags (key → typed value), the live credit balance, and the raw resolved entitlement list. Default = the user view (own subs + subs of orgs they belong to). Pass `?organizationId=` (member-only) for that org's view + shared pool. Requires the user JWT.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "organizationId",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/subscription": {
      "get": {
        "summary": "Get the current active subscription (the user's own, or an org's), or null",
        "tags": [
          "Public · Billing"
        ],
        "description": "Returns null when there is no active/pending/past-due subscription. Pass `?organizationId=` (member-only) to read an organization's subscription on an org-billed app. Requires the user JWT in X-Rekey-User-Token.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "organizationId",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/payments": {
      "get": {
        "summary": "List the calling end-user's own payments",
        "tags": [
          "Public · Billing"
        ],
        "description": "Payment history for the authenticated end-user only — newest first, default 50 (max 100 via ?limit=). Each row carries a `receiptUrl` when the provider receipt link is known, else null. Requires the user JWT in X-Rekey-User-Token.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/subscription/cancel": {
      "post": {
        "summary": "Cancel the calling end-user's current subscription",
        "tags": [
          "Public · Billing"
        ],
        "description": "Default = cancel at period end (provider-backed ACTIVE subscriptions stay ACTIVE with `cancelAt` set until the provider webhook terminates them). Pass `{\"atPeriodEnd\": false}` to cancel immediately. PENDING checkouts and subscriptions with no provider-side record are canceled locally right away. Requires the user JWT in X-Rekey-User-Token.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "atPeriodEnd": {
                    "type": "boolean",
                    "description": "true (default) = stop at period end; false = stop immediately."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/checkout": {
      "post": {
        "summary": "Start a checkout session for the current end-user",
        "tags": [
          "Public · Billing"
        ],
        "description": "Creates (or reuses) a PENDING Subscription locally and returns a provider-hosted checkout URL. Activation happens via the provider's webhook — not synchronously here.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "planSlug",
                  "successUrl",
                  "cancelUrl"
                ],
                "properties": {
                  "planSlug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "successUrl": {
                    "type": "string",
                    "format": "uri"
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri"
                  },
                  "couponCode": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40,
                    "description": "Optional coupon to apply. Validated server-side; a bad code rejects the whole checkout."
                  },
                  "organizationId": {
                    "type": "string",
                    "description": "Buy for an organization (owner+beneficiary). Caller must be OWNER/ADMIN of it."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [],
            "userToken": []
          },
          {
            "publishableKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/providers": {
      "get": {
        "summary": "List enabled billing providers for the calling application",
        "tags": [
          "Public · Billing"
        ],
        "description": "Returns providers configured + enabled for this Application, in the order the geo router would prefer them given the request country (CF-IPCountry header).",
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/coupons/validate": {
      "post": {
        "summary": "Validate a coupon for the current user against a plan",
        "tags": [
          "Public · Billing"
        ],
        "description": "Returns the discounted amount on success. Surfaces a precise RekeyError on every failure mode so the panel / pricing page can render a useful message.\n\nCallable from a browser with the publishable key, or from your server with a secret key carrying the `billing:read` scope. Either way the end-user JWT is required, because per-user redemption limits are checked against that user.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "planSlug"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "planSlug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/mfa/status": {
      "get": {
        "summary": "MFA status for the current user",
        "tags": [
          "Public · MFA"
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/mfa/setup": {
      "post": {
        "summary": "Mint a new TOTP secret + 10 backup codes (one-time-show). Not enrolled until /setup-confirm.",
        "tags": [
          "Public · MFA"
        ],
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/mfa/setup-confirm": {
      "post": {
        "summary": "Confirm MFA enrollment by entering the current 6-digit code",
        "tags": [
          "Public · MFA"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/mfa/challenge": {
      "post": {
        "summary": "Verify a TOTP or backup code (step-up auth). Returns { ok: bool }.",
        "tags": [
          "Public · MFA"
        ],
        "description": "Backup codes are single-use — consumed on success.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/auth/mfa/disable": {
      "post": {
        "summary": "Disable MFA for the current user",
        "tags": [
          "Public · MFA"
        ],
        "description": "Browser callers (publishable key) must send `code` — a current TOTP or an unused backup code. Server-side callers using an Application secret key are not required to, preserving the original contract.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "publishableKey": [],
            "userToken": []
          },
          {
            "apiKey": [],
            "userToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/licenses/verify": {
      "post": {
        "summary": "Verify a license key + record an activation for this machine",
        "tags": [
          "Public · Licenses"
        ],
        "description": "Returns { ok, license?, reason? }. `ok=false` is intentional for invalid licenses — the customer's software loops on this and we want a deterministic body, not an HTTP error.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key",
                  "machineFingerprint"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "machineFingerprint": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          },
          {
            "publishableKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/usage/record": {
      "post": {
        "summary": "Record a usage event against a named meter",
        "tags": [
          "Public · Usage"
        ],
        "description": "Requires an Application **secret** key with the `billing:write` scope (or `*`, the mint default). The publishable key is rejected — call this from your server.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "meterSlug",
                  "quantity"
                ],
                "properties": {
                  "meterSlug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "endUserId": {
                    "type": "string"
                  },
                  "organizationId": {
                    "type": "string"
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Optional. A retried record with the same (meter, key) returns the original UsageRecord instead of double-counting. Omit for each-call-counts behavior."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/usage/aggregate": {
      "get": {
        "summary": "Sum recorded quantity for a meter (with optional time window + end-user filter)",
        "tags": [
          "Public · Usage"
        ],
        "description": "Accepts a narrow `billing:read` key, since this only reads. A `billing:write` key also works, because write implies read. Secret key only; the publishable key is rejected.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 40
            },
            "in": "query",
            "name": "meterSlug",
            "required": true
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "in": "query",
            "name": "from",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "in": "query",
            "name": "to",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "endUserId",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "organizationId",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/credits/balance": {
      "get": {
        "summary": "Get a subject's current credit balance (end-user or org)",
        "tags": [
          "Public · Credits"
        ],
        "description": "Accepts a narrow `billing:read` key, since this only reads. A `billing:write` key also works, because write implies read. Secret key only; the publishable key is rejected.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "endUserId",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "organizationId",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/credits/consume": {
      "post": {
        "summary": "Deduct credits from a subject (idempotent)",
        "tags": [
          "Public · Credits"
        ],
        "description": "Atomically debits `amount` from the end-user or org pool. 402 CREDITS_INSUFFICIENT when too low. Pass `idempotencyKey` (or an `Idempotency-Key` header) to make retries safe.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "endUserId": {
                    "type": "string"
                  },
                  "organizationId": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/credits/ledger": {
      "get": {
        "summary": "List a subject's recent credit ledger entries (newest first)",
        "tags": [
          "Public · Credits"
        ],
        "description": "Accepts a narrow `billing:read` key, like `GET /balance`. Secret key only; the publishable key is rejected.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "endUserId",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "organizationId",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/portal/config/{slug}": {
      "get": {
        "summary": "Public config for the hosted customer portal of one Application",
        "tags": [
          "Public · Portal"
        ],
        "description": "Returns { slug, name, publishableKey, billingEnabled, branding } when the app has opted into the hosted portal. 404 otherwise (existence-hiding).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/.well-known/oauth-protected-resource": {
      "get": {
        "summary": "OAuth protected-resource metadata (RFC 9728)",
        "tags": [
          "MCP · OAuth"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/.well-known/oauth-authorization-server": {
      "get": {
        "summary": "OAuth authorization-server metadata (RFC 8414)",
        "tags": [
          "MCP · OAuth"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/.well-known/openid-configuration": {
      "get": {
        "summary": "OpenID Provider metadata (OIDC Discovery 1.0)",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "Present only for Applications with `authConfig.oidcEnabled`; 404 otherwise. Every advertised capability is implemented — unsupported OIDC features are advertised as unsupported rather than omitted.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/oauth/register": {
      "post": {
        "summary": "Dynamic client registration (RFC 7591)",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "Unauthenticated (RFC 7591 open registration). Registers a PUBLIC client — PKCE, no client secret is issued — so there is nothing to authenticate with yet at this point in the flow. Governed by `authConfig.dynamicClientRegistration` (default on): with it off this returns 403 `CLIENT_REGISTRATION_DISABLED` and the discovery documents stop advertising `registration_endpoint`. Turn it off once your relying parties are registered — on a public OpenID Provider, open registration lets anyone put a password form on the operator's own issuer origin.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "redirect_uris"
                ],
                "properties": {
                  "redirect_uris": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2048
                    }
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/oauth/authorize": {
      "get": {
        "summary": "Authorization endpoint — login + consent page",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "Renders an HTML sign-in + consent form for a browser. No Rekey credential — the end user authenticates by submitting the form below.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Submit login + consent",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "No Rekey credential — the end user's email + password (+ MFA code) travel in the form body and ARE the authentication.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/oauth/token": {
      "post": {
        "summary": "Token endpoint (RFC 6749 — authorization_code + refresh_token)",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "No Rekey credential and no client secret: clients here are public and prove themselves with PKCE. The `code` + `code_verifier` (or `refresh_token`) in the form body are the credential. An `id_token` (OIDC Core) is returned alongside the access token when the `openid` scope was granted — on the authorization_code grant only, never on a refresh.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/oauth/introspect": {
      "post": {
        "summary": "Token introspection (RFC 7662)",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "Requires this Application's own **secret** key as `Authorization: Bearer` — the handler verifies it and rejects a key belonging to any other Application with 401 `invalid_client`. The publishable key is not accepted: introspection reveals token state. Intended for a customer running their own MCP server against Rekey-issued end-user MCP tokens.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}/oauth/userinfo": {
      "get": {
        "summary": "OIDC UserInfo endpoint",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "Returns the claims authorised by the granted scopes for the end-user the access token was issued to: `sub` always, `email`/`email_verified` with the `email` scope, profile claims with `profile`. Requires the `openid` scope — a token without it gets 403 `insufficient_scope`. The token must have been issued by THIS Application; one from another Application is `invalid_token`.",
        "security": [
          {
            "endUserMcpToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "OIDC UserInfo endpoint",
        "tags": [
          "MCP · OAuth"
        ],
        "description": "Returns the claims authorised by the granted scopes for the end-user the access token was issued to: `sub` always, `email`/`email_verified` with the `email` scope, profile claims with `profile`. Requires the `openid` scope — a token without it gets 403 `insufficient_scope`. The token must have been issued by THIS Application; one from another Application is `invalid_token`.",
        "security": [
          {
            "endUserMcpToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/mcp/{slug}": {
      "post": {
        "summary": "MCP server endpoint (JSON-RPC 2.0)",
        "tags": [
          "MCP"
        ],
        "description": "Requires an **end-user** MCP access token (`Authorization: Bearer`), obtained through the OAuth flow above — NOT an Application key and not an operator credential. A missing or invalid token gets 401 plus a `WWW-Authenticate` header pointing at the protected-resource metadata.",
        "security": [
          {
            "endUserMcpToken": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "MCP endpoint — use POST (JSON-RPC)",
        "tags": [
          "MCP"
        ],
        "description": "Always 405 with `Allow: POST`. Checks no credential, because it never does any work — it exists so a GET-typer sees the method violation instead of a 404.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server/api/v1/mcp/{slug}": {
      "get": {
        "summary": "OAuth authorization-server metadata (RFC 8414, path-insertion form)",
        "tags": [
          "MCP · OAuth"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/.well-known/openid-configuration/api/v1/mcp/{slug}": {
      "get": {
        "summary": "OpenID Provider metadata (OIDC Discovery, path-insertion form)",
        "tags": [
          "MCP · OAuth"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/api/v1/mcp/{slug}": {
      "get": {
        "summary": "OAuth protected-resource metadata (RFC 9728, path-insertion form)",
        "tags": [
          "MCP · OAuth"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/webhook/stripe/{slug}": {
      "post": {
        "summary": "Receive a Stripe webhook event for a specific Application (BYO secret)",
        "tags": [
          "Webhooks · Stripe"
        ],
        "description": "The slug must match an Application with Stripe credentials (including a webhook signing secret) configured. The signature is verified against THAT Application's webhook secret — no events from other Stripe accounts will be accepted.\n\n**No bearer auth — the provider signature IS the authentication.** Do not send an `Authorization` header; the request is authenticated by verifying the raw body against the Application's own webhook signing secret. `security: []` here means \"no Rekey credential\", not \"unprotected\".",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/webhook/paypal/{slug}": {
      "post": {
        "summary": "Receive a PayPal webhook event for a specific Application (BYO webhook id)",
        "tags": [
          "Webhooks · PayPal"
        ],
        "description": "The slug must match an Application with PayPal credentials configured. Signature is verified against THAT Application's webhook id via PayPal's verify-webhook-signature API.\n\n**No bearer auth — the provider signature IS the authentication.** Do not send an `Authorization` header; the request is authenticated by verifying the raw body against the Application's own webhook signing secret. `security: []` here means \"no Rekey credential\", not \"unprotected\".",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/billing/webhook/razorpay/{slug}": {
      "post": {
        "summary": "Receive a Razorpay webhook event for a specific Application (BYO secret)",
        "tags": [
          "Webhooks · Razorpay"
        ],
        "description": "The slug must match an Application with Razorpay credentials (including a webhook secret) configured. The signature is verified against THAT Application's webhook secret (HMAC-SHA256) — no events from other Razorpay accounts will be accepted.\n\n**No bearer auth — the provider signature IS the authentication.** Do not send an `Authorization` header; the request is authenticated by verifying the raw body against the Application's own webhook signing secret. `security: []` here means \"no Rekey credential\", not \"unprotected\".",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/webhooks/billing/{provider}": {
      "post": {
        "summary": "Receive a billing-provider webhook event (provider-module pipeline)",
        "tags": [
          "Webhooks · Billing"
        ],
        "description": "Generic ingress for registered provider modules. The optional slug scopes the Application whose BYO credentials verify the signature; providers whose payloads carry `metadata.applicationId` (Stripe) may omit it. No bearer auth — the signature IS the auth.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/webhooks/billing/{provider}/{slug}": {
      "post": {
        "summary": "Receive a billing-provider webhook event (provider-module pipeline)",
        "tags": [
          "Webhooks · Billing"
        ],
        "description": "Generic ingress for registered provider modules. The optional slug scopes the Application whose BYO credentials verify the signature; providers whose payloads carry `metadata.applicationId` (Stripe) may omit it. No bearer auth — the signature IS the auth.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/signup-mode": {
      "get": {
        "summary": "Operator-registration mode for this deployment (open | invite | closed)",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "Public UX hint so the sign-up page can render the right state: an invite-key field (invite), a \"registration closed\" notice (closed), or the plain form (open). Not a secret — enforcement happens server-side at every creation path regardless.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/sign-up": {
      "post": {
        "summary": "Self-serve sign-up — creates an operator account, a Tenant, and an OWNER membership",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "Gated by OPERATOR_SIGNUP_MODE: open (anyone), invite (requires a single-use `inviteKey`), or closed (rejected). Mode is advertised at GET /signup-mode.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password",
                  "workspaceName"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 256
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "workspaceName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "inviteKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512,
                    "description": "Single-use invite key. Required when the deployment is in invite mode."
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/sign-in": {
      "post": {
        "summary": "Operator sign-in. Returns memberships + a session scoped to the first workspace.",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/mfa-verify": {
      "post": {
        "summary": "Exchange an operator MFA challenge token + code for a real session",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "Called after /sign-in returns `mfaRequired: true`. The challenge token expires after 5 minutes and is bound to the operator that just passed the primary factor.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mfaChallengeToken",
                  "code"
                ],
                "properties": {
                  "mfaChallengeToken": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048
                  },
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/refresh": {
      "post": {
        "summary": "Exchange a refresh token for a new pair (rotated)",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/sign-out": {
      "post": {
        "summary": "Revoke the presented refresh token (idempotent)",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/forgot-password": {
      "post": {
        "summary": "Request a password reset. Emailed to the operator; the raw token is returned only in dev or with no email transport.",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/reset-password": {
      "post": {
        "summary": "Consume a reset token + set new password (revokes all sessions)",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "newPassword"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/magic-link/request": {
      "post": {
        "summary": "Request a passwordless sign-in link. Emailed to the operator; the raw token is returned only in dev or with no email transport.",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/magic-link/verify": {
      "post": {
        "summary": "Consume a magic-link token + mint an operator session (or MFA challenge)",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/me": {
      "get": {
        "summary": "Current operator + memberships + active workspace",
        "tags": [
          "Tenant · Auth"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/switch-workspace": {
      "post": {
        "summary": "Switch the active workspace. Returns a new {access, refresh} pair scoped to the target.",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tenantId"
                ],
                "properties": {
                  "tenantId": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/sessions": {
      "get": {
        "summary": "List the operator's active sessions",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "Returns active sessions (live refresh tokens) ordered newest-first, with the User-Agent + IP captured at issue time. Use the returned `id` to revoke individual sessions via DELETE /sessions/:id.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/sessions/{id}": {
      "delete": {
        "summary": "Revoke one operator session by id. Idempotent.",
        "tags": [
          "Tenant · Auth"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/requests": {
      "get": {
        "summary": "Recent API requests made by the calling operator",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "The operator's own requests to the tenant API (the panel calls these on their behalf), newest first. Best-effort log capped per operator by a periodic pruner — a convenience tail, not a billing-grade audit trail. Paginated via ?limit&offset.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/sign-out-everywhere": {
      "post": {
        "summary": "Revoke every refresh token for the calling operator (logout all devices)",
        "tags": [
          "Tenant · Auth"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/change-password": {
      "post": {
        "summary": "Authenticated password change. Revokes other sessions on success.",
        "tags": [
          "Tenant · Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currentPassword",
                  "newPassword"
                ],
                "properties": {
                  "currentPassword": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/api-tokens": {
      "post": {
        "summary": "Mint a personal-access-token (raw shown once)",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nMints a long-lived, revocable, scoped operator PAT bound to the active workspace. Default-deny: omit `scopes` for read-only. Allowed scopes: read, applications:write, keys:mint. The `rawToken` is shown exactly once and cannot be recovered.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "read",
                        "applications:write",
                        "keys:mint"
                      ]
                    }
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "List the operator's active personal-access-tokens (redacted — no hash)",
        "tags": [
          "Tenant · Auth"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/api-tokens/{id}": {
      "delete": {
        "summary": "Revoke one of the operator's personal-access-tokens. Idempotent.",
        "tags": [
          "Tenant · Auth"
        ],
        "description": "No workspace role required — revocation is scoped to the calling operator, so this can only ever kill your own token. Deliberately looser than minting: an operator downgraded to MEMBER must still be able to revoke a PAT they already hold.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/invitations/preview": {
      "get": {
        "summary": "Preview an invitation by token (unauthenticated)",
        "tags": [
          "Tenant · Workspace"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            },
            "in": "query",
            "name": "token",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/invitations/accept": {
      "post": {
        "summary": "Accept an invitation. Returns a session scoped to the joined workspace.",
        "tags": [
          "Tenant · Workspace"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace": {
      "get": {
        "summary": "Get the active workspace",
        "tags": [
          "Tenant · Workspace"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a new workspace for the current operator (becomes OWNER)",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Lets a signed-in user spin up an additional Tenant without registering a new account. After creation, switch into it via POST /api/v1/tenant/auth/switch-workspace.\n\nA deployment can turn this off with `WORKSPACE_CREATION=disabled`, in which case this route (and only this route) refuses with 403 `WORKSPACE_CREATION_DISABLED`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Rename the active workspace",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/members": {
      "get": {
        "summary": "List members of the active workspace",
        "tags": [
          "Tenant · Workspace"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/members/{id}": {
      "delete": {
        "summary": "Remove a member from the workspace",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Change a member's role",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "role"
                ],
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "OWNER",
                      "ADMIN",
                      "MEMBER"
                    ]
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/members/{id}/grants": {
      "get": {
        "summary": "List a member's per-application grants",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Grant (or change) a MEMBER's role on one Application",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nUpserts the (member, application) grant: APP_ADMIN (full app read/write), APP_BILLING (read + billing/plans/coupons writes only), or APP_VIEWER (read-only). Only valid on MEMBER memberships — OWNER/ADMIN already have full access. The first grant a member receives switches them from legacy workspace-wide read access to grant-scoped access.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "applicationId",
                  "role"
                ],
                "properties": {
                  "applicationId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "APP_ADMIN",
                      "APP_BILLING",
                      "APP_VIEWER"
                    ]
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/members/{id}/grants/{applicationId}": {
      "delete": {
        "summary": "Remove a member's grant on one Application",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nCareful with the LAST grant: removing it returns the member to legacy workspace-wide read-only access (zero grants = pre-grants behavior), it does NOT lock them out.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "applicationId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/invitations": {
      "get": {
        "summary": "List invitations for this workspace",
        "tags": [
          "Tenant · Workspace"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create an invitation. Returns a one-time-show token to share via the link.",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "role"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "OWNER",
                      "ADMIN",
                      "MEMBER"
                    ]
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/invitations/{id}": {
      "delete": {
        "summary": "Revoke a pending invitation",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/workspace/email-logs": {
      "get": {
        "summary": "List recent SYSTEM email send-logs for the workspace (operator/invite mail, not per-app)",
        "tags": [
          "Tenant · Workspace"
        ],
        "description": "System mail only — operator magic-link/password-reset + workspace invitations (sends not tied to an Application). Per-application email logs live under the Application itself.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "sent",
                "error",
                "no_transport"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications": {
      "get": {
        "summary": "List Applications in the active workspace",
        "tags": [
          "Tenant · Applications"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create an Application in the active workspace",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nThe Application is the isolation boundary in Rekey — every row carries its `applicationId`. Create a separate Application per environment rather than mixing real and rehearsal data in one: `environment` is fixed here and cannot be changed afterwards.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "slug"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "PRODUCTION",
                      "STAGING",
                      "DEVELOPMENT"
                    ],
                    "default": "DEVELOPMENT",
                    "description": "What this Application is, fixed at creation and **immutable** — there is no endpoint that changes it. Defaults to DEVELOPMENT. A PRODUCTION app mints rp_live_ keys, others mint rp_test_ — the prefix is descriptive. Environment does NOT restrict which billing credentials the app may hold. To go live, create a PRODUCTION Application."
                  },
                  "billingProvider": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "razorpay",
                      "paypal"
                    ]
                  },
                  "enableBilling": {
                    "type": "boolean",
                    "description": "Create with the billing surface enabled. Defaults false."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/check-slug": {
      "get": {
        "summary": "Check if an Application slug is available",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Used by the \"Create application\" form for live availability feedback. Returns the same shape regardless — never leaks WHICH tenant owns a taken slug.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 40
            },
            "in": "query",
            "name": "slug",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}": {
      "get": {
        "summary": "Get one Application (must belong to the active workspace)",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/stats": {
      "get": {
        "summary": "Dashboard stats for one Application (Overview tiles)",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nEnd-user totals + 30-day sign-up trend, security-events summary, billing snapshot, and a usage/credits roll-up. Scoped to the active workspace.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/requests": {
      "get": {
        "summary": "Recent inbound API requests for an Application",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nRequires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nRequests made to this Application's public API with its secret key, newest first (status, route, duration, IP). Recorded best-effort by a global response hook; the table is capped per app by a periodic pruner, so this is a convenience tail, not a billing-grade audit trail. Paginated via ?limit&offset.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/auth-config": {
      "patch": {
        "summary": "Patch the auth configuration for an Application",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nToggle which auth methods are enabled (`password`, `magic_link`, …), set `signupEnabled=false` for invite-only apps, set the password minimum length, or update the redirect URL allowlist. OAuth provider availability is implicit from the per-provider oauth-config endpoints.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "methods": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 40
                    }
                  },
                  "passwordMinLength": {
                    "type": "integer",
                    "minimum": 8,
                    "maximum": 128
                  },
                  "redirectUrls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "appUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Base URL of your own application — what transactional emails link back to (the welcome mail CTA, and the base for reset/verify/magic-link URLs when the SDK call does not supply one). Send null or \"\" to clear it. When unset, and nothing else resolves (first redirectUrl origin, then DEFAULT_APP_URL), emails render WITHOUT the call-to-action button rather than with a broken link."
                  },
                  "organizationsEnabled": {
                    "type": "boolean"
                  },
                  "passwordBreachCheckEnabled": {
                    "type": "boolean",
                    "description": "HIBP Pwned-Passwords breach check at sign-up/reset/change. Default true."
                  },
                  "sendVerificationEmailOnSignUp": {
                    "type": "boolean",
                    "description": "Send the email-verification link automatically on password sign-up, alongside the welcome mail. Default true. Delivery is best-effort — a failed send never fails the sign-up."
                  },
                  "requireEmailVerification": {
                    "type": "boolean",
                    "description": "Refuse password sign-in with 403 EMAIL_NOT_VERIFIED until the end-user confirms their address. Default false; turning it on applies to existing unverified accounts immediately."
                  },
                  "signupEnabled": {
                    "type": "boolean",
                    "description": "Legacy alias for signupMode (false ⇔ invite_only). Prefer signupMode."
                  },
                  "signupMode": {
                    "type": "string",
                    "enum": [
                      "public",
                      "secret_only",
                      "invite_only"
                    ],
                    "description": "Who may create end-users: public (any key), secret_only (server-side secret key only — publishable key refused with SIGNUP_REQUIRES_SECRET_KEY), or invite_only (no public sign-up)."
                  },
                  "mfa": {
                    "type": "string",
                    "enum": [
                      "off",
                      "optional",
                      "required"
                    ],
                    "description": "End-user 2FA policy."
                  },
                  "mcpEnabled": {
                    "type": "boolean",
                    "description": "Expose a hosted MCP server + OAuth AS for this app."
                  },
                  "oidcEnabled": {
                    "type": "boolean",
                    "description": "Act as an OpenID Connect provider: serve /.well-known/openid-configuration, issue an id_token when the openid scope is granted, and expose /oauth/userinfo. Independent of mcpEnabled. The `email` scope additionally needs requireEmailVerification — Rekey will not assert an address nobody proved."
                  },
                  "dynamicClientRegistration": {
                    "type": "boolean",
                    "description": "Allow anyone to register an OAuth client with POST /oauth/register (RFC 7591 open registration). Default true — MCP clients self-register and there is no operator-side client-creation surface yet. Turn it off once your relying parties are registered: open registration on a public IdP lets anyone put a password prompt on this deployment's issuer origin."
                  },
                  "tokenAlg": {
                    "type": "string",
                    "enum": [
                      "HS256",
                      "RS256"
                    ],
                    "description": "Signature alg for NEW end-user access tokens. RS256 tokens verify offline against GET /.well-known/jwks.json; HS256 (default) requires the API. Switching never breaks outstanding tokens — the API verifies both."
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing-config": {
      "patch": {
        "summary": "Toggle billing for an Application",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nMaster switch. When disabled, the public billing API (checkout, subscriptions, coupons) returns 403 BILLING_DISABLED and the panel hides the Billing group. Default off for new apps.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "dunningEnabled": {
                    "type": "boolean",
                    "description": "Failed-payment recovery: reminders (day 0/3/7) + day-14 auto-cancel for PAST_DUE subscriptions. Off by default; opt in per app."
                  },
                  "billingSubject": {
                    "type": "string",
                    "enum": [
                      "user",
                      "org"
                    ],
                    "description": "Bill the individual end-user or their organization (owner+beneficiary)."
                  },
                  "defaultPlanSlug": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Free-tier fallback. Slug of an active plan whose FEATURE flags + included usage quota apply to end-users with no active subscription. null clears it."
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/api-keys": {
      "get": {
        "summary": "List active API keys for an application",
        "tags": [
          "Tenant · API Keys"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Mint an API key (raw shown once)",
        "tags": [
          "Tenant · API Keys"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nThe key's prefix follows the Application's `environment`: PRODUCTION mints `rp_live_…`, STAGING/DEVELOPMENT mint `rp_test_…`. It is not selectable.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/api-keys/{keyId}": {
      "delete": {
        "summary": "Revoke an API key",
        "tags": [
          "Tenant · API Keys"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "keyId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/plans": {
      "get": {
        "summary": "List Plans",
        "tags": [
          "Tenant · Plans"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a Plan",
        "tags": [
          "Tenant · Plans"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug",
                  "name",
                  "amount"
                ],
                "properties": {
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "MONTH",
                      "YEAR"
                    ]
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "SUBSCRIPTION",
                      "LICENSE",
                      "USAGE",
                      "CREDIT"
                    ]
                  },
                  "licenseKind": {
                    "type": "string",
                    "enum": [
                      "PERPETUAL",
                      "TIMED",
                      "SEATS"
                    ]
                  },
                  "licenseSeatsAllowed": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "licenseDurationDays": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "meterSlug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "pricePerUnitCents": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "creditsAmount": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/plans/{slug}": {
      "patch": {
        "summary": "Toggle a Plan's active flag",
        "tags": [
          "Tenant · Plans"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "active"
                ],
                "properties": {
                  "active": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/plans/{slug}/entitlements": {
      "get": {
        "summary": "List a plan's entitlements (the benefit bundle it grants)",
        "tags": [
          "Tenant · Plans"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Add or update one entitlement on a plan (upsert by kind+key)",
        "tags": [
          "Tenant · Plans"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "FEATURE",
                      "CREDIT",
                      "LICENSE",
                      "USAGE"
                    ]
                  },
                  "key": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "valueType": {
                    "type": "string",
                    "enum": [
                      "BOOL",
                      "INT",
                      "STRING"
                    ]
                  },
                  "value": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "quantity": {
                    "type": "integer"
                  },
                  "licenseKind": {
                    "type": "string",
                    "enum": [
                      "PERPETUAL",
                      "TIMED",
                      "SEATS"
                    ]
                  },
                  "rollover": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/plans/{slug}/entitlements/{entId}": {
      "delete": {
        "summary": "Remove an entitlement from a plan",
        "tags": [
          "Tenant · Plans"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "entId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/coupons": {
      "get": {
        "summary": "List Coupons (with redemption stats)",
        "tags": [
          "Tenant · Coupons"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nEach coupon carries `redemptionCount` and `totalDiscountIssued` (smallest currency unit) aggregated from the redemptions table.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a Coupon",
        "tags": [
          "Tenant · Coupons"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "discountType",
                  "amountOff"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "discountType": {
                    "type": "string",
                    "enum": [
                      "PERCENT",
                      "AMOUNT"
                    ]
                  },
                  "amountOff": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3
                  },
                  "planSlugs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "startsAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endsAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "maxRedemptions": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "maxRedemptionsPerUser": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/coupons/{code}": {
      "patch": {
        "summary": "Toggle a Coupon's active flag",
        "tags": [
          "Tenant · Coupons"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "active"
                ],
                "properties": {
                  "active": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "code",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing/providers": {
      "get": {
        "summary": "Discover all registered billing provider modules (+ per-app status)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nOne entry per REGISTERED provider module (in registry order), whether or not this Application has configured it: display metadata (label, docs URL, default countries, priority), capabilities, and the credential field schema the panel renders forms from. `status` is null until the provider is configured for this Application. Never returns credential values — those are write-only.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing-credentials": {
      "get": {
        "summary": "List all billing providers configured for this Application",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nReturns one entry per configured provider with its enabled flag, country list, and priority. Never returns the credentials themselves — those are write-only.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing-credentials/{provider}": {
      "put": {
        "summary": "Set or rotate BYO credentials for one provider",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nBody shape depends on `provider` (fields come from the provider module registry):\n  stripe → { data: { apiKey, webhookSecret }, countries?, priority?, enabled? }\n  razorpay → { data: { keyId, keySecret, webhookSecret }, countries?, priority?, enabled? }\n  paypal → { data: { clientId, clientSecret, webhookId }, countries?, priority?, enabled? }\n\nCredentials are AES-256-GCM encrypted at rest. There is no GET that returns plaintext.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "stripe",
                "razorpay",
                "paypal"
              ]
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Update routing or enabled flag for one provider (no secret rotation)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "countries": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 2,
                      "maxLength": 2
                    }
                  },
                  "priority": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "test",
                      "live"
                    ]
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "stripe",
                "razorpay",
                "paypal"
              ]
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Remove credentials for one provider entirely",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "stripe",
                "razorpay",
                "paypal"
              ]
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing-credentials/{provider}/register-webhook": {
      "post": {
        "summary": "Auto-configure the provider webhook via its API (no manual paste)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nCreates the webhook endpoint at this Application's Rekey URL and stores the returned signing secret (Stripe) / webhook id (PayPal) into the credentials. Save the provider credentials first. Razorpay is not supported — configure it manually.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "stripe",
                "razorpay",
                "paypal"
              ]
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing-credentials/webhook-events": {
      "get": {
        "summary": "List recent INBOUND provider webhook events (Stripe/PayPal/Razorpay)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nThe events Rekey received from the billing provider (subscription activated, payment captured, etc.). Filter by `?provider=`. This is the inbound log — distinct from outbound webhook deliveries (this Application's own /webhooks endpoints).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "stripe",
                "razorpay",
                "paypal"
              ]
            },
            "in": "query",
            "name": "provider",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/billing/stats": {
      "get": {
        "summary": "Revenue / subscription stats for this Application (Billing Overview tiles)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nSubscription counters (active, past-due, canceled/new in the last 30 days), MRR (ACTIVE recurring SUBSCRIPTION plans, yearly normalized to monthly), 30-day payment volume + success/failure counts, and a 12-month UTC monthly revenue series. All amounts are in the smallest currency unit.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/payments": {
      "get": {
        "summary": "List payments for this Application (newest first)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nOperator view of every Payment row — subscription invoices and one-time charges. Filter by `status` and a `from`/`to` createdAt window. Joined with the paying end-user's email where the payment is attributable to one. Sort with `?sort=createdAt|amount|status&order=asc|desc` (default createdAt desc).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "SUCCEEDED",
                "FAILED",
                "REFUNDED"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "in": "query",
            "name": "from",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "in": "query",
            "name": "to",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "amount",
                "status"
              ]
            },
            "in": "query",
            "name": "sort",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "in": "query",
            "name": "order",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/dunning": {
      "get": {
        "summary": "List dunning cases for this Application (newest first)",
        "tags": [
          "Tenant · Billing"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nFailed-payment recovery cases. A case opens when a subscription goes PAST_DUE, sends reminder emails on day 0/3/7, and exhausts on day 14 (subscription canceled). The provider drives the actual card retries — see docs/billing.md → Dunning. Filter with `?status=`; sort with `?sort=openedAt|nextActionAt|status&order=asc|desc` (default openedAt desc).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "OPEN",
                "RECOVERED",
                "EXHAUSTED",
                "CANCELED"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "openedAt",
                "nextActionAt",
                "status"
              ]
            },
            "in": "query",
            "name": "sort",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "in": "query",
            "name": "order",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/oauth-config/{provider}": {
      "put": {
        "summary": "Set or rotate an OAuth provider config (clientId + clientSecret + redirectUri)",
        "tags": [
          "Tenant · OAuth"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nclientSecret is encrypted at rest. Public bits (clientId, redirectUri, scopes, issuerUrl) live in `oauthConfig`. Built-in providers: google, github, microsoft, discord, gitlab, slack, oidc. For `oidc`, also pass `issuerUrl` (e.g. https://login.example.com) — endpoints are auto-discovered.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clientId",
                  "clientSecret",
                  "redirectUri"
                ],
                "properties": {
                  "clientId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "clientSecret": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  },
                  "redirectUri": {
                    "type": "string",
                    "format": "uri"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "issuerUrl": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Remove an OAuth provider config",
        "tags": [
          "Tenant · OAuth"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users": {
      "get": {
        "summary": "List recent end-users (for license issuance pickers etc.)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nSort with `?sort=createdAt|email&order=asc|desc` (default createdAt desc).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "maxLength": 254
            },
            "in": "query",
            "name": "search",
            "required": false
          },
          {
            "schema": {
              "type": "boolean"
            },
            "in": "query",
            "name": "emailVerified",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "ACTIVE",
                "PAST_DUE",
                "CANCELED",
                "EXPIRED"
              ]
            },
            "in": "query",
            "name": "subscriptionStatus",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "email"
              ]
            },
            "in": "query",
            "name": "sort",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "in": "query",
            "name": "order",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create an end-user manually (operator-driven)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nUse this for support seeding / data migrations. The SDK's public sign-up endpoint is the normal path. Password is optional — if omitted, the user can only sign in via OAuth or via password-reset flow. Marks the email verified by default since an operator vouched.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  },
                  "role": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "emailVerified": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users/{euid}": {
      "get": {
        "summary": "Get one end-user with their passkeys + recent impersonation audits",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Patch an end-user (role, metadata, verified flag)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nEmail is immutable (it's the natural key per-Application). To change a password use the password-reset flow. Pass `metadata: null` to clear; pass an object to overwrite — partial merges aren't supported because Json columns can't deep-merge atomically.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "metadata": {},
                  "emailVerified": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Delete an end-user (cascade), or GDPR-erase them with ?erasure=true (tombstone + retain financials)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "in": "query",
            "name": "erasure",
            "required": false,
            "description": "When \"true\", performs a GDPR erasure (tombstone + anonymized financial retention) instead of a hard cascade delete. OWNER/ADMIN only."
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users/{euid}/billing": {
      "get": {
        "summary": "Get an end-user's subscriptions, payments + licenses (operator billing view)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users/{euid}/credits": {
      "get": {
        "summary": "Get an end-user's credit balance + recent ledger",
        "tags": [
          "Tenant · Credits"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users/{euid}/credits/grant": {
      "post": {
        "summary": "Manually grant / refund / adjust an end-user's credits",
        "tags": [
          "Tenant · Credits"
        ],
        "description": "Requires **billing-write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` or `APP_BILLING` grant on it.\n\nPositive `amount` adds credits (GRANT / REFUND). Negative `amount` with reason ADJUST removes them (refused if it would overdraw). Idempotent on `idempotencyKey` when provided.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "integer"
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "GRANT",
                      "REFUND",
                      "ADJUST"
                    ]
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users/{euid}/export": {
      "get": {
        "summary": "Export everything stored about one end-user as JSON (GDPR/DSAR)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nRequires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nReturns a downloadable JSON document: profile, OAuth identities, session metadata (no token material), MFA enrollment metadata (no secrets), passkey metadata, organization memberships, subscriptions, payments, licenses (key prefix only), credit balance + ledger, usage records (capped — see `notes`), security events, and impersonation audits. OWNER/ADMIN only.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-users/{euid}/impersonate": {
      "post": {
        "summary": "Mint a short-lived impersonation token for an end-user (audited)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.\n\nRequires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).\n\nOperator-as-user access token, 5-minute lifetime, no refresh. Every minting writes an `impersonation_audits` row. Use sparingly — every action taken with this token is attributed to the end-user in their own activity logs, with the operator id in the JWT `imp` claim for downstream attribution.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 280,
                    "description": "Short justification, captured in audit."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/rotate-sessions": {
      "post": {
        "summary": "Force-logout every end-user of this Application (session kill-switch)",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nBumps the app token generation (invalidating all live end-user access + MFA-challenge tokens) and revokes every active refresh token. End-users must sign in again. Irreversible.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/rotate-public-key": {
      "post": {
        "summary": "Rotate the publishable key (dual-key grace window)",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nMints a new rp_pub_ key and keeps the old one valid for `graceDays` (default 30, max 90) so clients shipped with the old key keep working until you redeploy. Roll the new key out to your frontends/installs during the window.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "graceDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 90,
                    "default": 30
                  },
                  "force": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/portal": {
      "patch": {
        "summary": "Update hosted customer portal settings (enable/disable, branding)",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "branding": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "portalDomain": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/access": {
      "get": {
        "summary": "Get the Application IP allowlist + CORS origins",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Set the Application IP allowlist (secret-key calls) + CORS origins",
        "tags": [
          "Tenant · Applications"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nipAllowlist: CIDRs/IPs that server-side secret keys must call from (empty = allow all). corsOrigins: browser origins the SDK calls from, folded into the API CORS allowlist. Each field is replace-in-full when provided.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ipAllowlist": {
                    "type": "array",
                    "maxItems": 200,
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64,
                      "pattern": "^[0-9a-fA-F:.\\/]+$"
                    }
                  },
                  "corsOrigins": {
                    "type": "array",
                    "maxItems": 100,
                    "items": {
                      "type": "string",
                      "maxLength": 256,
                      "pattern": "^https?://[^/\\s]+$"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-user-roles": {
      "get": {
        "summary": "List the role catalog for an Application",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Add a role to the catalog",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 40
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 240
                  },
                  "isDefault": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/end-user-roles/{name}": {
      "patch": {
        "summary": "Update a role (description, default flag)",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": "string",
                    "maxLength": 240
                  },
                  "isDefault": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "name",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Delete a role; pass ?reassignTo=name to bulk-move users in one transaction",
        "tags": [
          "Tenant · End-users"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 40
            },
            "in": "query",
            "name": "reassignTo",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "name",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/licenses": {
      "get": {
        "summary": "List licenses for an Application",
        "tags": [
          "Tenant · Licenses"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Issue a license to an end-user",
        "tags": [
          "Tenant · Licenses"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nReturns the raw key in `data.rawKey` — show ONCE. PERPETUAL/TIMED/SEATS kinds. Customer apps validate via POST /api/v1/licenses/verify.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endUserId",
                  "kind"
                ],
                "properties": {
                  "endUserId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "PERPETUAL",
                      "TIMED",
                      "SEATS"
                    ]
                  },
                  "planId": {
                    "type": "string"
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "seatsAllowed": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/licenses/{licenseId}": {
      "delete": {
        "summary": "Revoke a license",
        "tags": [
          "Tenant · Licenses"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "licenseId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/usage-meters": {
      "get": {
        "summary": "List usage meters",
        "tags": [
          "Tenant · Usage"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a usage meter",
        "tags": [
          "Tenant · Usage"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug",
                  "name",
                  "unit"
                ],
                "properties": {
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "unit": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/usage-meters/{slug}": {
      "patch": {
        "summary": "Toggle a usage meter active/inactive",
        "tags": [
          "Tenant · Usage"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Permanently delete a usage meter (cascades to records)",
        "tags": [
          "Tenant · Usage"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/organizations": {
      "get": {
        "summary": "List end-user organizations in this Application",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create an organization (optionally seed an initial OWNER)",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "slug"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "ownerEndUserId": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/organizations/{orgId}": {
      "get": {
        "summary": "Get one organization with its members + pending invitations",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Delete an organization (cascades to memberships + invitations)",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "patch": {
        "summary": "Update an organization (name / metadata)",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/organizations/{orgId}/members": {
      "post": {
        "summary": "Add an existing end-user to an organization with a role",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endUserId",
                  "role"
                ],
                "properties": {
                  "endUserId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "OWNER",
                      "ADMIN",
                      "MEMBER"
                    ]
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/organizations/{orgId}/members/{euid}": {
      "patch": {
        "summary": "Change an organization member's role",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "role"
                ],
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "OWNER",
                      "ADMIN",
                      "MEMBER"
                    ]
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Remove an end-user from an organization",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "euid",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/organizations/{orgId}/billing": {
      "get": {
        "summary": "Org billing summary — entitlements, shared credit pool, beneficiary subscriptions",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/organizations/{orgId}/licenses/{licenseId}/rotate-key": {
      "post": {
        "summary": "Mint + reveal the raw key for an org-pooled license (shown once)",
        "tags": [
          "Tenant · Organizations"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.\n\nOrg-pooled license keys are issued during provisioning and stored hash-only, so the raw key is never readable afterwards. This mints a NEW key for the existing pooled license and returns it in `data.rawKey` — show ONCE. Use it to hand the org its key so the team can validate via POST /api/v1/licenses/verify. Rotating resets the key hash and clears existing activations (`data.activationsReset`).",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "orgId",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "licenseId",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/operator/applications": {
      "get": {
        "summary": "List Applications in the PAT's workspace (requires `read` scope)",
        "tags": [
          "Tenant · Operator PAT"
        ],
        "security": [
          {
            "operatorPat": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/operator/applications/{id}/api-keys": {
      "get": {
        "summary": "List active API keys for an application (requires `read` scope)",
        "tags": [
          "Tenant · Operator PAT"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "operatorPat": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Mint an Application API key via an operator PAT (requires `keys:mint` scope)",
        "tags": [
          "Tenant · Operator PAT"
        ],
        "description": "Mints an Application secret key. The PAT must carry the `keys:mint` scope and be bound to the workspace that owns the application. The `rawKey` is shown exactly once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "operatorPat": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/.well-known/oauth-authorization-server": {
      "get": {
        "summary": "Authorization-server metadata (RFC 8414)",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/.well-known/oauth-protected-resource": {
      "get": {
        "summary": "Protected-resource metadata (RFC 9728)",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/oauth/register": {
      "post": {
        "summary": "Dynamic client registration (RFC 7591)",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "description": "Unauthenticated by design (RFC 7591 open registration). Registers a PUBLIC client — PKCE, no client secret — so there is nothing to authenticate with yet.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "redirect_uris"
                ],
                "properties": {
                  "redirect_uris": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2048
                    }
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/oauth/authorize": {
      "get": {
        "summary": "Authorization endpoint → panel consent",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "description": "Redirects the browser into the panel consent screen. No credential — the operator may not be signed in yet; the panel handles that and then calls /oauth/grant.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/oauth/grant": {
      "post": {
        "summary": "Mint an authorization code (panel consent)",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "description": "Called by the panel after the operator approves. Requires an operator **session** access token (not a PAT) — the session bearer doubles as the CSRF guard, since it cannot be forged cross-site. The operator's membership of the requested workspace is confirmed before the code is minted.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/oauth/token": {
      "post": {
        "summary": "Token endpoint",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "description": "No Rekey credential and no client secret: clients are public and prove themselves with PKCE. The `code` + `code_verifier` (or `refresh_token`) in the form body are the credential.",
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp/oauth/introspect": {
      "post": {
        "summary": "Token introspection (RFC 7662). Authenticate with an operator PAT.",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "description": "Requires an operator PAT. Answers only about tokens in the caller's own workspace; an unknown, expired, or out-of-workspace token returns `{ active: false }`.",
        "security": [
          {
            "operatorPat": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/mcp": {
      "post": {
        "summary": "Operator MCP JSON-RPC endpoint",
        "tags": [
          "MCP · Operator"
        ],
        "description": "Single JSON-RPC 2.0 POST. `Authorization: Bearer` accepts **either** credential:\n\n- an operator personal-access-token (`rp_op_…`) that carries the `read` scope, or\n- an OAuth-issued access JWT (`typ: op_mcp_access`) from this deployment’s operator-MCP authorization server (start at `GET /api/v1/tenant/mcp/oauth/authorize`).\n\nOne bearer at a time; credentials are never chained. Either way, the operator’s workspace membership is re-checked against the database on every request, and the tools can only ever see that one workspace.\n\n**Read tools** need nothing beyond authenticating. **Write tools** additionally require write capability on the presented credential — `mcp:operator:write` in the OAuth scope, or the `applications:write` scope on a PAT — AND an operator role that clears the tool’s floor (ADMIN by default). The gate filters `tools/list` as well as `tools/call`, so an under-privileged credential does not even see them. Destructive / financial tools are gated further on `mcp:operator:admin`, which only the OAuth path can carry — a PAT can never reach them.",
        "security": [
          {
            "mcpAccessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "Operator MCP endpoint — use POST (JSON-RPC)",
        "tags": [
          "MCP · Operator"
        ],
        "description": "Always 405 with `Allow: POST`. Still authenticated: the plugin-level Bearer hook runs first, so an unauthenticated GET gets 401 rather than this 405.",
        "security": [
          {
            "mcpAccessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server/api/v1/tenant/mcp": {
      "get": {
        "summary": "Authorization-server metadata (RFC 8414, path-insertion form)",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/api/v1/tenant/mcp": {
      "get": {
        "summary": "Protected-resource metadata (RFC 9728, path-insertion form)",
        "tags": [
          "MCP · Operator · OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-config": {
      "get": {
        "summary": "Get an Application's email config and effective transport",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-credentials": {
      "put": {
        "summary": "Set or rotate the Application's BYO email transport (Resend or SMTP) + sender",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fromAddress"
                ],
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "resend",
                      "smtp"
                    ]
                  },
                  "apiKey": {
                    "type": "string",
                    "maxLength": 256
                  },
                  "host": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "port": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 65535
                  },
                  "secure": {
                    "type": "boolean"
                  },
                  "user": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "pass": {
                    "type": "string",
                    "maxLength": 512
                  },
                  "fromAddress": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "fromName": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "replyTo": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Revert this Application to the default (or no) email transport",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-logs": {
      "get": {
        "summary": "List recent email send-logs for this Application",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "sent",
                "error",
                "no_transport"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-templates": {
      "get": {
        "summary": "List customisable email events",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-templates/{eventKey}": {
      "get": {
        "summary": "Get the template for one event",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "eventKey",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Upsert a customised template for one event",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "eventKey",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Revert one event to the built-in default template",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "eventKey",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-templates/{eventKey}/preview": {
      "post": {
        "summary": "Render a template against sample data (no email is sent)",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "eventKey",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/email-templates/{eventKey}/test-send": {
      "post": {
        "summary": "Render the template with sample values and send to a chosen address",
        "tags": [
          "Tenant · Email"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "eventKey",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/webhooks": {
      "get": {
        "summary": "List webhook endpoints for an Application",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a webhook endpoint. Returns the signing secret once.",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/webhooks/{endpointId}": {
      "patch": {
        "summary": "Update an endpoint",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "endpointId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "delete": {
        "summary": "Remove an endpoint",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "endpointId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/webhooks/{endpointId}/rotate-secret": {
      "post": {
        "summary": "Rotate the endpoint's signing secret. Returns the new value once.",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "endpointId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/webhooks/{endpointId}/deliveries": {
      "get": {
        "summary": "List recent delivery attempts for an endpoint",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **read** access to this Application — OWNER/ADMIN, or a MEMBER holding any grant on it (grant-less legacy members keep workspace-wide read).",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "endpointId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/applications/{id}/webhooks/{endpointId}/deliveries/{deliveryId}/retry": {
      "post": {
        "summary": "Force a re-attempt of a failed delivery",
        "tags": [
          "Tenant · Webhooks"
        ],
        "description": "Requires **write** access to this Application — OWNER/ADMIN, or a MEMBER with an `APP_ADMIN` grant on it.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "endpointId",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "deliveryId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/mfa/status": {
      "get": {
        "summary": "MFA status for the operator",
        "tags": [
          "Tenant · MFA"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/mfa/setup": {
      "post": {
        "summary": "Mint a new TOTP secret + 10 backup codes",
        "tags": [
          "Tenant · MFA"
        ],
        "description": "Returns the otpauth URI for QR + backup codes (one-time-show). Not enrolled until /setup-confirm.",
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/mfa/setup-confirm": {
      "post": {
        "summary": "Confirm enrollment with the current TOTP code",
        "tags": [
          "Tenant · MFA"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/mfa/disable": {
      "post": {
        "summary": "Disable MFA for the operator",
        "tags": [
          "Tenant · MFA"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/security-events": {
      "get": {
        "summary": "List recent security events for the active workspace",
        "tags": [
          "Tenant · Security"
        ],
        "description": "Requires the **OWNER or ADMIN** workspace role.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "applicationId",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "in": "query",
            "name": "type",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "operator",
                "end_user",
                "system"
              ]
            },
            "in": "query",
            "name": "actorType",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "in": "query",
            "name": "from",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "in": "query",
            "name": "to",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "type"
              ]
            },
            "in": "query",
            "name": "sort",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "in": "query",
            "name": "order",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ]
            },
            "in": "query",
            "name": "format",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/passkeys": {
      "get": {
        "summary": "List operator passkeys",
        "tags": [
          "Tenant · Passkeys"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/passkeys/register/start": {
      "post": {
        "summary": "Begin a registration ceremony for the current operator",
        "tags": [
          "Tenant · Passkeys"
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/passkeys/register/complete": {
      "post": {
        "summary": "Complete a registration ceremony; stores the credential",
        "tags": [
          "Tenant · Passkeys"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "response",
                  "expectedChallenge"
                ],
                "properties": {
                  "response": {
                    "type": "object"
                  },
                  "expectedChallenge": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  },
                  "deviceName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/passkeys/{id}": {
      "delete": {
        "summary": "Remove a passkey from the current operator",
        "tags": [
          "Tenant · Passkeys"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "tenantSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/passkeys/authenticate/start": {
      "post": {
        "summary": "Begin a passkey sign-in ceremony for an operator (usernameless)",
        "tags": [
          "Tenant · Passkeys"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/passkeys/authenticate/complete": {
      "post": {
        "summary": "Complete a passkey sign-in; mints a session",
        "tags": [
          "Tenant · Passkeys"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "response",
                  "expectedChallenge"
                ],
                "properties": {
                  "response": {
                    "type": "object"
                  },
                  "expectedChallenge": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  }
                }
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/oauth/providers": {
      "get": {
        "summary": "List operator OAuth providers configured on this deployment",
        "tags": [
          "Tenant · OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/oauth/{provider}/start": {
      "post": {
        "summary": "Begin operator OAuth sign-in — returns the provider authorization URL",
        "tags": [
          "Tenant · OAuth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "state"
                ],
                "properties": {
                  "state": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/tenant/auth/oauth/{provider}/callback": {
      "post": {
        "summary": "Complete operator OAuth sign-in; mints a session (or an MFA challenge)",
        "tags": [
          "Tenant · OAuth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4096
                  },
                  "inviteKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "provider",
            "required": true
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/tenants": {
      "get": {
        "summary": "List all tenants",
        "tags": [
          "Admin · Tenants"
        ],
        "description": "Returns Tenants newest-first (paginated). Bootstrap-admin only.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a tenant",
        "tags": [
          "Admin · Tenants"
        ],
        "description": "Creates a new Tenant. The first call also bootstraps the system. Subsequent Applications are created under POST /api/v1/admin/tenants/:id/applications.\n\nOptional `limits` sets this workspace's ceilings at creation and overrides the deployment-wide `DEFAULT_TENANT_LIMITS`. Omit it to take that default, which is unlimited unless the deployment configured one.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "ownerEmail"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "ownerEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "limits": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/tenants/{id}": {
      "get": {
        "summary": "Get a tenant by id",
        "tags": [
          "Admin · Tenants"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/tenants/{id}/limits": {
      "get": {
        "summary": "Get a tenant's resource limits and current usage",
        "tags": [
          "Admin · Tenants"
        ],
        "description": "Returns the workspace's ceilings plus what is currently counted against them.\n\nAn omitted or `null` limit means **unlimited** — the default for every workspace, so a deployment that never sets limits is unconstrained.\n\n`usage.activeEndUsers` counts non-erased EndUsers across **all** Applications in the workspace (limits are workspace-wide, not per-Application). `usage.productionApps` counts Applications whose `environment` is `PRODUCTION`; staging and development Applications are never counted.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "put": {
        "summary": "Set a tenant's resource limits",
        "tags": [
          "Admin · Tenants"
        ],
        "description": "Replaces the workspace's limits wholesale (PUT, not PATCH — an omitted key becomes unlimited). Send `{}` to clear every limit.\n\nRequires `SUPER_ADMIN_KEY`: this is a deployment-level control, and a workspace operator must not be able to raise their own ceiling.\n\nLimits gate **creation** only. Setting a limit below current usage is allowed and never signs anyone out or takes an application offline — existing end-users keep working and existing production applications keep serving traffic. New sign-ups (and new production applications) fail with `TENANT_QUOTA_EXCEEDED` until usage drops below the line.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "maxActiveEndUsers": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Max non-erased EndUsers across every Application in this workspace. Null or omitted = unlimited."
                  },
                  "maxProductionApps": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Max Applications in this workspace with `environment: PRODUCTION`. Staging and development Applications are never counted and never blocked. Null or omitted = unlimited."
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications": {
      "get": {
        "summary": "List applications",
        "tags": [
          "Admin · Applications"
        ],
        "description": "Optionally filter by `tenantId`.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "tenantId",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create an application under a tenant",
        "tags": [
          "Admin · Applications"
        ],
        "description": "Mints a unique slug, public key, and default auth/billing config. Use POST /api/v1/admin/applications/:id/api-keys to mint a secret key for the SDK.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tenantId",
                  "name",
                  "slug"
                ],
                "properties": {
                  "tenantId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40,
                    "description": "URL-safe lowercase identifier. Letters, digits, hyphens only."
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "PRODUCTION",
                      "STAGING",
                      "DEVELOPMENT"
                    ],
                    "description": "Defaults to DEVELOPMENT. Write-once — it cannot be changed after creation, so create a separate Application per environment. Only PRODUCTION counts against the workspace `maxProductionApps` limit."
                  },
                  "billingProvider": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "paypal",
                      "razorpay"
                    ]
                  },
                  "enableBilling": {
                    "type": "boolean",
                    "description": "Provision with the billing surface enabled. Defaults false."
                  },
                  "authConfig": {
                    "type": "object",
                    "properties": {
                      "methods": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "enum": [
                            "password",
                            "google",
                            "github",
                            "magic_link"
                          ]
                        }
                      },
                      "passwordMinLength": {
                        "type": "integer",
                        "minimum": 8
                      },
                      "redirectUrls": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "uri"
                        }
                      },
                      "organizationsEnabled": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}": {
      "get": {
        "summary": "Get an application by id",
        "tags": [
          "Admin · Applications"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}/api-keys": {
      "get": {
        "summary": "List active API keys for an application",
        "tags": [
          "Admin · API Keys"
        ],
        "description": "Returns key metadata only. The raw key value is unrecoverable after creation.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Mint an API key for an application",
        "tags": [
          "Admin · API Keys"
        ],
        "description": "Returns the raw key in `data.rawKey`. **Show this to the operator exactly once** — we only store its SHA-256 hash, so the value is unrecoverable after this response.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Human-readable label, e.g. \"CI server\", \"Staging worker\"."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Defaults to `[\"*\"]` (full access). Use `auth:read`, `billing:write`, etc., to scope."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Optional ISO-8601 expiry. Omit for non-expiring keys."
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}/api-keys/{keyId}": {
      "delete": {
        "summary": "Revoke an API key",
        "tags": [
          "Admin · API Keys"
        ],
        "description": "Soft-revokes the key (sets `revokedAt`). Idempotent.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "keyId",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}/plans": {
      "get": {
        "summary": "List plans for an application",
        "tags": [
          "Admin · Plans"
        ],
        "parameters": [
          {
            "schema": {
              "type": "boolean"
            },
            "in": "query",
            "name": "includeInactive",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a plan",
        "tags": [
          "Admin · Plans"
        ],
        "description": "Creates a Plan locally, then registers it with Stripe if the Application has Stripe credentials stored. PayPal and Razorpay register the plan lazily at first checkout. Amount is in the smallest currency unit (cents/paise/sen — never a decimal float).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug",
                  "name",
                  "amount"
                ],
                "properties": {
                  "slug": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Smallest currency unit (e.g. cents)."
                  },
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "description": "ISO 4217. Defaults to USD."
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "MONTH",
                      "YEAR"
                    ]
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}/plans/{slug}": {
      "patch": {
        "summary": "Toggle a plan's active flag",
        "tags": [
          "Admin · Plans"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "active"
                ],
                "properties": {
                  "active": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "slug",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}/coupons": {
      "get": {
        "summary": "List coupons for an application",
        "tags": [
          "Admin · Coupons"
        ],
        "parameters": [
          {
            "schema": {
              "type": "boolean"
            },
            "in": "query",
            "name": "includeInactive",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "post": {
        "summary": "Create a coupon",
        "tags": [
          "Admin · Coupons"
        ],
        "description": "Discount kinds: PERCENT (amountOff in basis points, so 1500 = 15%) or AMOUNT (amountOff in smallest currency unit). Codes are stored lowercase.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "discountType",
                  "amountOff"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "discountType": {
                    "type": "string",
                    "enum": [
                      "PERCENT",
                      "AMOUNT"
                    ]
                  },
                  "amountOff": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3
                  },
                  "planSlugs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "startsAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endsAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "maxRedemptions": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "maxRedemptionsPerUser": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/applications/{id}/coupons/{code}": {
      "patch": {
        "summary": "Toggle a coupon's active flag",
        "tags": [
          "Admin · Coupons"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "active"
                ],
                "properties": {
                  "active": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "code",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/operator-invites": {
      "post": {
        "summary": "Mint a single-use operator-invite key (raw key shown once)",
        "tags": [
          "Admin · Operator Invites"
        ],
        "description": "Returns the raw key in `data.rawToken`. **Show it to the recipient exactly once** — only its SHA-256 hash is stored, so the value is unrecoverable after this response. The key authorizes ONE new operator sign-up while OPERATOR_SIGNUP_MODE=invite.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "note": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Optional human label, e.g. \"for jane@acme.com\"."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Optional ISO-8601 expiry. Omit for a non-expiring (still single-use) key."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      },
      "get": {
        "summary": "List operator-invite keys (newest first, paginated; never returns the hash)",
        "tags": [
          "Admin · Operator Invites"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "in": "query",
            "name": "offset",
            "required": false
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/operator-invites/{id}": {
      "delete": {
        "summary": "Revoke an unused operator-invite key. Idempotent.",
        "tags": [
          "Admin · Operator Invites"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/overview": {
      "get": {
        "summary": "Deployment-wide rollup",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "Tenants, applications, end-users, orgs, subscriptions, payments, MRR, webhook + API-request health over the last 24h/30d.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/services": {
      "get": {
        "summary": "Service health",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "Live DB + Redis ping, outbound webhook 24h success rate, oldest unprocessed inbound webhook.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/retention": {
      "get": {
        "summary": "Active users & signup trend",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "End-user & operator active counts over 24h/7d/30d (refresh-token proxy) plus a 14-day end-user signup histogram.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/tenants": {
      "get": {
        "summary": "Per-tenant summary (searchable + sortable)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/applications": {
      "get": {
        "summary": "Per-application summary (searchable + sortable)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/end-users": {
      "get": {
        "summary": "End-users (search by email/id)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/tenant-users": {
      "get": {
        "summary": "Operator accounts (search by email/name/id)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/security-events": {
      "get": {
        "summary": "Security audit (filter by actor/type/ip)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/api-requests": {
      "get": {
        "summary": "API request log (filter by status/method/path)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/payments": {
      "get": {
        "summary": "Payments (filter by status, search by id/provider-id)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/subscriptions": {
      "get": {
        "summary": "Subscriptions (filter by status)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/webhook-events": {
      "get": {
        "summary": "Inbound webhook events (filter by provider, onlyFailed)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/webhook-deliveries": {
      "get": {
        "summary": "Outbound webhook deliveries (filter by status/endpoint)",
        "tags": [
          "Admin · Metrics"
        ],
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/credit-liability": {
      "get": {
        "summary": "Outstanding prepaid credits",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "SUM(CreditBalance.balance) deployment-wide + top-20 applications by outstanding balance.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/locked-accounts": {
      "get": {
        "summary": "End-user accounts in lockout",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "End-users currently locked by the Redis brute-force limiter (`bf:lock:eu:login:*`) — failed-sign-in protection currently engaged.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/email-deliverability": {
      "get": {
        "summary": "Email transport rollup",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "EmailLog status counts over the last 24h + 7d, plus top-5 applications by error count (7d).",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/webhook-endpoint-health": {
      "get": {
        "summary": "Per-endpoint webhook health (24h)",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "Outbound deliveries aggregated by endpoint — success rate + retry-storm flag. Sorted by failure count.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    },
    "/api/v1/admin/metrics/payments-by-app": {
      "get": {
        "summary": "Payment health per application (30d)",
        "tags": [
          "Admin · Metrics"
        ],
        "description": "Payments aggregated by application + status over the last 30 days. Surfaces apps whose provider integration is failing.",
        "security": [
          {
            "superAdminKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response"
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "https://api.rekey.dev",
      "description": "Production"
    },
    {
      "url": "http://localhost:3030",
      "description": "Local development"
    }
  ]
}