{"openapi":"3.1.0","info":{"title":"Locksmith Public API","version":"1.0.0","description":"JWT authentication API for end-user apps. Every request (except magic-link verification) is scoped to a **project** and **environment** via the API key.\n\n## API keys and environments\n\n| Prefix | Environment |\n|--------|--------------|\n| `lsm_live_` | Production — counts toward plan user limits |\n| `lsm_sbx_` | Sandbox — isolated data, no plan user limit |\n\nPass the key in the `X-API-Key` header. Do not expose it in browser code.\n\n## CORS\n\nIf the project has `allowedOrigins` configured, the `Origin` header must match. Empty list allows any origin (convenient for local development).\n\n## Response shape\n\nSuccessful responses are wrapped as `{ \"data\": ... }`. Errors are `{ \"error\": \"<code>\", \"message\": \"...\" }`.\n\n## Rate limits (representative)\n\n| Endpoint | Window | Limit | Key |\n|----------|--------|-------|-----|\n| POST /api/auth/signup | 1 hour | 10 | IP |\n| POST /api/auth/login | 15 min | 10 | IP + email |\n| POST /api/auth/magic-link | 1 hour | 5 | IP + email |\n| POST /api/auth/refresh | 1 min | 30 | IP |\n| POST /api/auth/password/reset | 1 hour | 5 | IP + email |\n\nOn limit exceeded: HTTP 429, error code `rate_limited`.\n\n## Social sign-in (OAuth 2.0)\n\nAfter you enable a provider in the dashboard:\n\n1. `POST /api/auth/oauth/{provider}` — returns `authorizationUrl`; redirect the user’s browser there.\n2. The IdP redirects to Locksmith’s callback; Locksmith then redirects to your app with `?code=…` (exchange code) and `provider`.\n3. Your **server** calls `POST /api/auth/oauth/token` with that code (and `X-API-Key`) to obtain Locksmith **access** and **refresh** tokens.\n\nProvider slug examples: `google`, `github`, `discord` (see dashboard for the full list). Initiation errors still use the normal `{ error, message }` envelope.\n\n## Hosted SSO (OIDC)\n\n**Pro plan** projects act as an OIDC provider. Discovery: `GET /api/oidc/{projectId}/.well-known/openid-configuration`. The authorization endpoint redirects users to **your** login URL with a signed `request_token`; after login and consent, your backend calls `POST /api/auth/oidc/grant` to obtain a `redirectUrl` for the user’s browser. Standard `/token` and `/userinfo` endpoints follow OIDC/OAuth2 semantics and may return `{ error, error_description }` (not the `data` wrapper) on failure.\n\n# Official SDKs\n\nUse these clients from **your backend** (never expose your API key in a browser). Each SDK sends `X-API-Key` and parses Locksmith’s `{ \"data\": … }` envelope.\n\nSource lives in the **Locksmith monorepo** under `sdks/` (mirrored to per-language GitHub repos). Full READMEs ship in each SDK folder.\n\n### TypeScript / JavaScript (`@getlocksmith/sdk`)\n\n```bash\nnpm install @getlocksmith/sdk\n```\n\n```typescript\nimport { LocksmithClient } from '@getlocksmith/sdk'\n\nconst auth = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\nconst { user, accessToken } = await auth.signIn({\n  email: 'user@example.com',\n  password: 'secure-password',\n})\nconst me = await auth.getUser(accessToken)\n```\n\nOptional: `import { createMiddleware } from '@getlocksmith/sdk/adapters/next'`, `@getlocksmith/sdk/adapters/trpc`.\n\n### Python (`locksmith-py`)\n\n```bash\npip install locksmith-py\n```\n\n```python\nimport os\nfrom locksmith import LocksmithClient\n\nc = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\nr = c.sign_in(email=\"user@example.com\", password=\"secure-password\")\nme = c.get_user(r[\"accessToken\"])\n```\n\n### Go (`github.com/locksmith-app/sdk-go`)\n\n```bash\ngo get github.com/locksmith-app/sdk-go@latest\n```\n\n```go\nimport (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nc, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nout, _ := c.SignIn(\"user@example.com\", \"secure-password\")\n_, _ = c.GetUser(out.AccessToken)\n```\n\n### Rust (crate `getlocksmith`)\n\n```toml\ngetlocksmith = \"0.1\"\n```\n\n```rust\nuse getlocksmith::LocksmithClient;\n\nlet c = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet r = c.sign_in(\"user@example.com\", \"secret\").await?;\n```\n\n### Ruby (`locksmith-ruby`)\n\n```ruby\ngem \"locksmith-ruby\"\n```\n\n```ruby\nrequire \"locksmith\"\nc = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = c.sign_in(email: \"user@example.com\", password: \"secure-password\")\n```\n\n### PHP (Composer `locksmith/sdk-php`)\n\n```bash\ncomposer require locksmith/sdk-php\n```\n\n```php\nuse Locksmith\\LocksmithClient;\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->signIn('user@example.com', 'secure-password');\n```\n\n### C# / .NET (`Locksmith.Sdk`)\n\n```bash\ndotnet add package Locksmith.Sdk\n```\n\n```csharp\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar bundle = await client.SignInAsync(\"user@example.com\", \"secure-password\");\nvar access = bundle.GetProperty(\"accessToken\").GetString();\n```\n\n### Java (`app.locksmith:locksmith-java`)\n\n```java\ntry (var c = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = c.signIn(\"user@example.com\", \"secure-password\");\n    var user = c.getUser(data.get(\"accessToken\").asText());\n}\n```\n\n### Kotlin (`app.locksmith` — JVM)\n\n```kotlin\nval c = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = c.signIn(\"user@example.com\", \"secure-password\")\nval user = c.getUser(data[\"accessToken\"].asText())\n```\n\n### Dart (`locksmith_dart`)\n\n```dart\nimport 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal c = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await c.signIn('user@example.com', 'secure-password');\n```\n\n### Elixir (`locksmith_ex`)\n\n```elixir\nc = Locksmith.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.sign_in(c, \"user@example.com\", \"secure-password\")\n```\n\n### Swift (SwiftPM product `Locksmith`)\n\n```swift\nimport Locksmith\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet bundle = try await client.signIn(email: \"user@example.com\", password: \"secure-password\")\n```\n\n---\n\n# Frameworks\n\nFramework packages sit on top of the HTTP API: they keep **`X-API-Key`** on the server, optional **cookie BFF** sessions, and UI tuned for that stack.\n\n## Next.js\n\n```bash\nnpm install @getlocksmith/nextjs next react react-dom\n```\n\n- **Server:** `import { createLocksmithRouteHandlers, locksmithServerClientFromEnv, createLocksmithMiddleware } from '@getlocksmith/nextjs/server'`\n- **Client (React):** `import { LocksmithAuthProvider, LocksmithSignInForm, LocksmithSignUpForm } from '@getlocksmith/nextjs/client'`\n\nMount **one** catch‑all route (e.g. `app/api/locksmith/[[...path]]/route.ts`) with `createLocksmithRouteHandlers`, then wrap your app in `LocksmithAuthProvider` using the same `routePrefix`. The browser talks only to your origin; the handler forwards to Locksmith with the API key and sets **httpOnly** cookies.\n\nFull setup, env vars, themes, Free‑plan “Powered by” behavior, and passkeys: see the package README on [npm](https://www.npmjs.com/package/@getlocksmith/nextjs) or `sdks/nextjs/README.md` in the monorepo.\n\n---\n\n# OAuth & OIDC with SDKs\n\nAfter enabling a provider in the dashboard, start OAuth from your **backend**:\n\n| | |\n|--|--|\n| **TypeScript** | `await auth.initiateOAuth({ provider: 'github' })` → redirect browser → `await auth.exchangeOAuthCode(code)` |\n| **Go** | `InitiateOAuth` / `ExchangeOAuthCode` |\n| **Ruby** | `initiate_oauth` / `exchange_oauth_code` |\n| **PHP** | `initiateOAuth` / `exchangeOAuthCode` |\n| **Rust** | `initiate_oauth` / `exchange_oauth_code` (async) |\n| **Dart** | `initiateOAuth` / `exchangeOAuthCode` |\n\n**Hosted SSO (Pro):** complete the grant from your login UI with `completeOidcGrant` / `complete_oidc_grant` / `CompleteOidcGrant` (see OpenAPI `POST /api/auth/oidc/grant`).\n\n---\n\n# Verify JWTs locally\n\nAccess tokens are **RS256** JWTs. Fetch your project’s **public PEM** from the dashboard, then:\n\n- **TypeScript:** `auth.verifyToken(accessToken, publicKeyPem)`\n- **Python:** `c.verify_token(accessToken, public_key_pem)`\n- **Go:** `locksmith.VerifyToken(accessToken, pem)`\n- **Rust:** `getlocksmith::LocksmithClient::verify_token(&token, pem)`\n- **Ruby:** `c.verify_token(access_token, pem)`\n- **PHP:** `verifyTokenLocal($access, $pem)`\n- **C#:** `LocksmithClient.VerifyToken(accessToken, pem)`\n- **Dart:** `c.verifyToken(accessToken, pem)`\n- **Elixir:** `Locksmith.verify_token(access_token, pem)`\n\nSwift ships `decodeTokenPayload` for issuer-checked payload; add full crypto verification for production hardening."},"servers":[{"url":"{baseUrl}","description":"Your Locksmith deployment","variables":{"baseUrl":{"default":"https://getlocksmith.dev"}}}],"tags":[{"name":"Auth","description":"Sign up, sign in, tokens, and current user"},{"name":"Magic link","description":"Passwordless email sign-in"},{"name":"Password","description":"Reset flow"},{"name":"OAuth","description":"Social / IdP sign-in for end users (dashboard-configured providers)"},{"name":"OIDC","description":"Hosted SSO — project as OIDC provider (Pro). Grant bridge uses Auth tag."}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Project API key (`lsm_live_…` or `lsm_sbx_…`)."},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"RS256 access token issued by Locksmith for this project and environment."}},"schemas":{"ApiError":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string"}}},"SignupRequest":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8,"description":"Minimum 8 characters."},"meta":{"type":"object","additionalProperties":true,"description":"Optional initial metadata; merged with project default meta."}}},"LoginRequest":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}},"RefreshLogoutRequest":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string","description":"Opaque refresh token from sign-in or refresh."}}},"UserSignup":{"type":"object","required":["id","email","role","meta","createdAt"],"properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string"},"meta":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"UserLogin":{"type":"object","required":["id","email","role","meta","lastLoginAt"],"properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string"},"meta":{"type":"object","additionalProperties":true},"lastLoginAt":{"type":["string","null"],"format":"date-time"}}},"UserMe":{"type":"object","required":["id","email","role","meta","emailVerified","createdAt","lastLoginAt"],"properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string"},"meta":{"type":"object","additionalProperties":true},"emailVerified":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"lastLoginAt":{"type":["string","null"],"format":"date-time"}}},"TokenBundle":{"type":"object","required":["accessToken","refreshToken","expiresIn"],"properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"integer","description":"Access token TTL in seconds."}}},"SignupResponseData":{"type":"object","required":["user","accessToken","refreshToken","expiresIn"],"properties":{"user":{"$ref":"#/components/schemas/UserSignup"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"integer"}}},"LoginResponseData":{"type":"object","required":["user","accessToken","refreshToken","expiresIn"],"properties":{"user":{"$ref":"#/components/schemas/UserLogin"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"integer"}}},"MeResponseData":{"type":"object","required":["user"],"properties":{"user":{"$ref":"#/components/schemas/UserMe"}}},"LogoutResponseData":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","const":true}}},"MagicLinkRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"createIfNotExists":{"type":"boolean","default":true,"description":"If true, creates a user without password when sending the link."}}},"MagicLinkVerifyUser":{"type":"object","required":["id","email","role","meta","createdAt"],"properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string"},"meta":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"MagicLinkVerifyData":{"type":"object","required":["user","accessToken","refreshToken","expiresIn"],"properties":{"user":{"$ref":"#/components/schemas/MagicLinkVerifyUser"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"integer"}}},"PasswordResetRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"PasswordUpdateRequest":{"type":"object","required":["token","newPassword"],"properties":{"token":{"type":"string","description":"Opaque token from the reset email."},"newPassword":{"type":"string","minLength":8}}},"SuccessBoolean":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","const":true}}},"OAuthInitiateRequest":{"type":"object","properties":{"redirectUrl":{"type":["string","null"],"format":"uri","description":"Where Locksmith redirects after OAuth; overrides per-provider and project defaults when set."}}},"OAuthInitiateData":{"type":"object","required":["provider","authorizationUrl"],"properties":{"provider":{"type":"string","description":"Same as path `{provider}`."},"authorizationUrl":{"type":"string","format":"uri","description":"Redirect the user here."}}},"OAuthTokenRequest":{"type":"object","required":["code"],"properties":{"code":{"type":"string","description":"Single-use exchange code from your redirect URL query string."}}},"OAuthExchangeUser":{"type":"object","required":["id","email","role","meta","createdAt"],"properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string"},"meta":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"OAuthTokenResponseData":{"type":"object","required":["user","accessToken","refreshToken","expiresIn","provider"],"properties":{"user":{"$ref":"#/components/schemas/OAuthExchangeUser"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"integer","description":"Access token TTL in seconds."},"provider":{"type":"string","description":"OAuth provider slug."}}},"OidcGrantRequest":{"type":"object","required":["requestToken","approved"],"properties":{"requestToken":{"type":"string","description":"Signed token from the `/authorize` redirect (query `request_token`)."},"userId":{"type":"string","description":"Locksmith user id when `approved` is true."},"approved":{"type":"boolean","description":"If false, user denied consent — no code is issued."},"scopes":{"type":"array","items":{"type":"string"},"description":"Optional subset of requested scopes; must include `openid` when approved."}}},"OidcGrantResponseData":{"type":"object","required":["redirectUrl"],"properties":{"redirectUrl":{"type":"string","format":"uri","description":"Open in the user’s browser to finish the OIDC flow."}}},"OidcOAuthStyleError":{"type":"object","description":"Some OIDC endpoints return OAuth 2.0 style errors in JSON (no `data` wrapper).","properties":{"error":{"type":"string"},"error_description":{"type":"string"}}}},"responses":{"ValidationError":{"description":"Invalid body (Zod)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"examples":{"default":{"value":{"error":"validation_error","message":"Invalid email address."}}}}}},"UnauthorizedApiKey":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"RateLimited":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/api/auth/signup":{"post":{"tags":["Auth"],"summary":"Sign up","description":"Create a user in this project and environment. Returns tokens immediately. Production users count toward plan limits.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { user, accessToken, refreshToken, expiresIn } = await client.signUp({\n  email: 'user@example.com',\n  password: 'secure-password',\n  meta: { plan: 'free' },\n})"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ndata = client.sign_up(\n    email=\"user@example.com\",\n    password=\"secure-password\",\n    meta={\"plan\": \"free\"},\n)"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, err := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nif err != nil {\n    panic(err)\n}\nout, err := client.SignUp(\"user@example.com\", \"secure-password\", map[string]interface{}{\"plan\": \"free\"})\nif err != nil {\n    panic(err)\n}\n_, _, _ = out.User, out.AccessToken, out.RefreshToken"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet bundle = client\n    .sign_up(\n        \"user@example.com\",\n        \"secure-password\",\n        Some(serde_json::json!({ \"plan\": \"free\" })),\n    )\n    .await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.sign_up(\n  email: \"user@example.com\",\n  password: \"secure-password\",\n  meta: { \"plan\" => \"free\" },\n)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->signUp('user@example.com', 'secure-password', ['plan' => 'free']);"},{"label":"C# SDK","lang":"csharp","source":"using System.Collections.Generic;\nusing System.Text.Json;\nusing Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar meta = JsonSerializer.SerializeToElement(new Dictionary<string, string> { [\"plan\"] = \"free\" });\nvar data = await client.SignUpAsync(\"user@example.com\", \"secure-password\", meta);"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = client.signUp(\"user@example.com\", \"secure-password\");\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = client.signUp(\"user@example.com\", \"secure-password\")"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.signUp('user@example.com', 'secure-password', {'plan': 'free'});"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.Client.sign_up(client, \"user@example.com\", \"secure-password\", %{\"plan\" => \"free\"})"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.signUp(\n    email: \"user@example.com\",\n    password: \"secure-password\",\n    meta: [\"plan\": \"free\"]\n)"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SignupResponseData"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedApiKey"},"403":{"description":"`user_banned` or `plan_limit_reached`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"`user_already_exists`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/auth/login":{"post":{"tags":["Auth"],"summary":"Sign in","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { user, accessToken, refreshToken, expiresIn } = await client.signIn({\n  email: 'user@example.com',\n  password: 'secure-password',\n})"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ndata = client.sign_in(email=\"user@example.com\", password=\"secure-password\")"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nout, err := client.SignIn(\"user@example.com\", \"secure-password\")\nif err != nil {\n    panic(err)\n}\n_, _, _ = out.User, out.AccessToken, out.RefreshToken"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet bundle = client.sign_in(\"user@example.com\", \"secure-password\").await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.sign_in(email: \"user@example.com\", password: \"secure-password\")"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->signIn('user@example.com', 'secure-password');"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar data = await client.SignInAsync(\"user@example.com\", \"secure-password\");"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = client.signIn(\"user@example.com\", \"secure-password\");\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = client.signIn(\"user@example.com\", \"secure-password\")"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.signIn('user@example.com', 'secure-password');"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.Client.sign_in(client, \"user@example.com\", \"secure-password\")"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.signIn(email: \"user@example.com\", password: \"secure-password\")"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/LoginResponseData"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"description":"`invalid_credentials` (same message whether email or password is wrong) or `invalid_api_key`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"`user_banned`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"summary":"Sign out","description":"Revokes the refresh token session if found. Always returns success (idempotent) to avoid leaking session state.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nawait client.signOut(refreshToken)"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\nclient.sign_out(refresh_token)"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\n_ = client.SignOut(refreshToken)"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nclient.sign_out(refresh_token).await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\nclient.sign_out(refresh_token)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$client->signOut($refreshToken);"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nawait client.SignOutAsync(refreshToken);"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    client.signOut(refreshToken);\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nclient.signOut(refreshToken)"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nawait client.signOut(refreshToken);"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\nLocksmith.Client.sign_out(client, refresh_token)"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\ntry await client.signOut(refreshToken: refreshToken)"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshLogoutRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/LogoutResponseData"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedApiKey"}}}},"/api/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh tokens","description":"Exchanges a refresh token for a new access token and a **new** refresh token. The previous refresh token is invalidated. Replay detection revokes all sessions for the user.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { accessToken, refreshToken: newRefresh, expiresIn } = await client.refresh(refreshToken)"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ntokens = client.refresh(refresh_token)"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\ntokens, err := client.Refresh(refreshToken)\nif err != nil {\n    panic(err)\n}\n_, _ = tokens.AccessToken, tokens.RefreshToken"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet tokens = client.refresh(refresh_token).await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.refresh(refresh_token)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->refresh($refreshToken);"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar data = await client.RefreshAsync(refreshToken);"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = client.refresh(refreshToken);\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = client.refresh(refreshToken)"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.refresh(refreshToken);"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.Client.refresh(client, refresh_token)"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.refresh(refreshToken: refreshToken)"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshLogoutRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TokenBundle"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"description":"`invalid_api_key` or `invalid_refresh_token`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/auth/me":{"get":{"tags":["Auth"],"summary":"Current user","description":"Requires `X-API-Key` **and** `Authorization: Bearer <access_token>`. Loads the live user row after verifying the JWT.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst user = await client.getUser(accessToken)"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\nme = client.get_user(access_token)"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nme, err := client.GetUser(accessToken)\nif err != nil {\n    panic(err)\n}\n_ = me.Email"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet me = client.get_user(access_token).await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\nme = client.get_user(access_token)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$user = $client->getUser($accessToken);"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar user = await client.GetUserAsync(accessToken);\nvar email = user.GetProperty(\"email\").GetString();"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var user = client.getUser(accessToken);\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval user = client.getUser(accessToken)"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal user = await client.getUser(accessToken);"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\nme = Locksmith.Client.get_user(client, access_token)"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet user = try await client.getUser(accessToken: accessToken)"}],"security":[{"ApiKeyAuth":[],"BearerAuth":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/MeResponseData"}}}}}},"401":{"description":"`missing_api_key`, `invalid_api_key`, or `invalid_token`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"`user_banned`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/magic-link":{"post":{"tags":["Magic link"],"summary":"Send magic link","description":"Always returns `{ data: { success: true } }` to prevent email enumeration.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nawait client.sendMagicLink('user@example.com', { createIfNotExists: true })"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\nclient.send_magic_link(\"user@example.com\", create_if_not_exists=True)"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nt := true\n_ = client.SendMagicLink(\"user@example.com\", &t)"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nclient.send_magic_link(\"user@example.com\", Some(true)).await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\nclient.send_magic_link(\"user@example.com\", create_if_not_exists: true)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$client->sendMagicLink('user@example.com', true);"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nawait client.SendMagicLinkAsync(\"user@example.com\", createIfNotExists: true);"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    client.sendMagicLink(\"user@example.com\", true);\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nclient.sendMagicLink(\"user@example.com\", createIfNotExists = true)"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nawait client.sendMagicLink('user@example.com', createIfNotExists: true);"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\nLocksmith.Client.send_magic_link(client, \"user@example.com\", create_if_not_exists: true)"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\ntry await client.sendMagicLink(email: \"user@example.com\", createIfNotExists: true)"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MagicLinkRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SuccessBoolean"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedApiKey"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/auth/magic-link/verify":{"get":{"tags":["Magic link"],"summary":"Verify magic link","description":"Opened from the email link. **No API key.** Query: `token` (raw), `project` (project id). CORS follows project `allowedOrigins`.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\n// No X-API-Key on this request; client still supplies baseUrl.\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { user, accessToken, refreshToken, expiresIn } = await client.verifyMagicLink({\n  token: rawTokenFromEmail,\n  projectId: 'clxxxxxxxx',\n})"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ndata = client.verify_magic_link(token=raw_token, project_id=\"clxxxxxxxx\")"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nout, err := client.VerifyMagicLink(rawToken, projectID)\nif err != nil {\n    panic(err)\n}\n_, _ = out.User, out.AccessToken"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet bundle = client.verify_magic_link(raw_token, \"clxxxxxxxx\").await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.verify_magic_link(token: raw_token, project_id: \"clxxxxxxxx\")"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->verifyMagicLink($rawToken, 'clxxxxxxxx');"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar data = await client.VerifyMagicLinkAsync(rawToken, projectId);"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = client.verifyMagicLink(rawToken, \"clxxxxxxxx\");\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = client.verifyMagicLink(rawToken, \"clxxxxxxxx\")"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.verifyMagicLink(token: rawToken, projectId: 'clxxxxxxxx');"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.Client.verify_magic_link(client, raw_token, \"clxxxxxxxx\")"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.verifyMagicLink(token: rawToken, projectId: \"clxxxxxxxx\")"}],"security":[],"parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"},"description":"Opaque token from the email."},{"name":"project","in":"query","required":true,"schema":{"type":"string"},"description":"Project id (cuid)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/MagicLinkVerifyData"}}}}}},"401":{"description":"`invalid_magic_link`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"`user_not_found` (rare edge case)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/password/reset":{"post":{"tags":["Password"],"summary":"Request password reset","description":"Always returns `{ data: { success: true } }` to prevent email enumeration.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nawait client.sendPasswordReset('user@example.com')"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\nclient.send_password_reset(\"user@example.com\")"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\n_ = client.SendPasswordReset(\"user@example.com\")"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nclient.send_password_reset(\"user@example.com\").await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\nclient.send_password_reset(\"user@example.com\")"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$client->sendPasswordReset('user@example.com');"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nawait client.SendPasswordResetAsync(\"user@example.com\");"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    client.sendPasswordReset(\"user@example.com\");\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nclient.sendPasswordReset(\"user@example.com\")"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nawait client.sendPasswordReset('user@example.com');"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\nLocksmith.Client.send_password_reset(client, \"user@example.com\")"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\ntry await client.sendPasswordReset(email: \"user@example.com\")"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SuccessBoolean"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedApiKey"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/auth/password/update":{"post":{"tags":["Password"],"summary":"Complete password reset","description":"After success, all refresh sessions for the user are revoked.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nawait client.updatePassword({\n  token: tokenFromEmail,\n  newPassword: 'new-secure-password',\n})"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\nclient.update_password(token=token_from_email, new_password=\"new-secure-password\")"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\n_ = client.UpdatePassword(tokenFromEmail, \"new-secure-password\")"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nclient.update_password(token_from_email, \"new-secure-password\").await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\nclient.update_password(token: token_from_email, new_password: \"new-secure-password\")"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$client->updatePassword($tokenFromEmail, 'new-secure-password');"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nawait client.UpdatePasswordAsync(tokenFromEmail, \"new-secure-password\");"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    client.updatePassword(tokenFromEmail, \"new-secure-password\");\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nclient.updatePassword(tokenFromEmail, \"new-secure-password\")"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nawait client.updatePassword(token: tokenFromEmail, newPassword: 'new-secure-password');"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\nLocksmith.Client.update_password(client, token_from_email, \"new-secure-password\")"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\ntry await client.updatePassword(token: tokenFromEmail, newPassword: \"new-secure-password\")"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordUpdateRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SuccessBoolean"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"description":"`invalid_api_key` or `invalid_reset_token`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/oauth/{provider}":{"post":{"tags":["OAuth"],"summary":"Start OAuth sign-in","description":"Returns an authorization URL. Redirect the user’s browser there. The provider must be configured and enabled for this project. Optional JSON body sets `redirectUrl` (otherwise dashboard / project defaults apply).","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { authorizationUrl, provider } = await client.initiateOAuth({\n  provider: 'github',\n  redirectUrl: 'https://myapp.com/oauth/callback',\n})\n\n// Redirect the user's browser to authorizationUrl"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ndata = client.initiate_oauth(provider=\"github\", redirect_url=\"https://myapp.com/oauth/callback\")\nauthorization_url = data[\"authorizationUrl\"]"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nru := \"https://myapp.com/oauth/callback\"\nout, err := client.InitiateOAuth(\"github\", &ru)\nif err != nil {\n    panic(err)\n}\n_ = out.AuthorizationURL"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet start = client\n    .initiate_oauth(\"github\", Some(\"https://myapp.com/oauth/callback\"))\n    .await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.initiate_oauth(\n  provider: \"github\",\n  redirect_url: \"https://myapp.com/oauth/callback\",\n)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->initiateOAuth('github', 'https://myapp.com/oauth/callback');"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar data = await client.InitiateOAuthAsync(\"github\", \"https://myapp.com/oauth/callback\");"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = client.initiateOAuth(\"github\", \"https://myapp.com/oauth/callback\");\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = client.initiateOAuth(\"github\", redirectUrl = \"https://myapp.com/oauth/callback\")"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.initiateOAuth('github', redirectUrl: 'https://myapp.com/oauth/callback');"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.Client.initiate_oauth(client, \"github\", redirect_url: \"https://myapp.com/oauth/callback\")"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.initiateOAuth(\n    provider: \"github\",\n    redirectUrl: \"https://myapp.com/oauth/callback\"\n)"}],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"},"example":"google","description":"Provider slug (e.g. google, github)."}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthInitiateRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/OAuthInitiateData"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedApiKey"}}}},"/api/auth/oauth/{provider}/callback":{"get":{"tags":["OAuth"],"summary":"OAuth callback (browser)","description":"Handled by the identity provider’s redirect. **Do not call from your backend.** On success, redirects to your `redirect_url` with `code` and `provider` query params (and optional `state`). On failure may redirect with `error` or return JSON/text errors.","security":[],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"code","in":"query","required":false,"schema":{"type":"string"},"description":"Authorization code from the IdP."},{"name":"state","in":"query","required":false,"schema":{"type":"string"},"description":"Locksmith state JWT."}],"responses":{"302":{"description":"Redirect to developer app with exchange `code` or error."},"400":{"description":"Validation or state error (body format varies)."}}}},"/api/auth/oauth/token":{"post":{"tags":["OAuth"],"summary":"Exchange OAuth code for session","description":"Server-side only. Exchange the `code` from your redirect URL for Locksmith RS256 access + refresh tokens.","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { user, accessToken, refreshToken, expiresIn, provider } =\n  await client.exchangeOAuthCode(codeFromQueryString)"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ndata = client.exchange_oauth_code(code_from_query_string)"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nout, err := client.ExchangeOAuthCode(codeFromQuery)\nif err != nil {\n    panic(err)\n}\n_, _, _ = out.User, out.AccessToken, out.Provider"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet bundle = client.exchange_oauth_code(code_from_query).await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.exchange_oauth_code(code_from_query_string)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->exchangeOAuthCode($codeFromQuery);"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar data = await client.ExchangeOAuthCodeAsync(codeFromQuery);"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var data = client.exchangeOAuthCode(codeFromQuery);\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval data = client.exchangeOAuthCode(codeFromQuery)"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.exchangeOAuthCode(codeFromQuery);"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata = Locksmith.Client.exchange_oauth_code(client, code_from_query)"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.exchangeOAuthCode(codeFromQuery)"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/OAuthTokenResponseData"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"description":"`invalid_api_key` or `invalid_token` (bad exchange code)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"`user_banned`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"`user_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/oidc/grant":{"post":{"tags":["Auth","OIDC"],"summary":"Complete OIDC authorization (grant bridge)","description":"After your login UI receives `request_token` from `/api/oidc/{projectId}/authorize`, authenticate the user, then call this endpoint to get the final `redirectUrl` for the browser. **Requires Pro** (`feature_not_available` on lower tiers).","x-codeSamples":[{"label":"TypeScript SDK","lang":"typescript","source":"import { LocksmithClient } from '@getlocksmith/sdk'\n\nconst client = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })\n\nconst { redirectUrl } = await client.completeOidcGrant({\n  requestToken,\n  approved: true,\n  userId: 'user_cuid',\n  scopes: ['openid', 'profile', 'email'],\n})"},{"label":"Python SDK","lang":"python","source":"import os\nfrom locksmith import LocksmithClient\n\nclient = LocksmithClient(api_key=os.environ[\"LOCKSMITH_API_KEY\"])\ndata = client.complete_oidc_grant(\n    request_token=request_token,\n    approved=True,\n    user_id=\"user_cuid\",\n    scopes=[\"openid\", \"profile\", \"email\"],\n)\nredirect_url = data[\"redirectUrl\"]"},{"label":"Go SDK","lang":"go","source":"import (\n    \"os\"\n\n    locksmith \"github.com/locksmith-app/sdk-go\"\n)\n\nclient, _ := locksmith.NewClient(os.Getenv(\"LOCKSMITH_API_KEY\"), \"\")\nuid := \"user_cuid\"\nscopes := []string{\"openid\", \"profile\", \"email\"}\nout, err := client.CompleteOidcGrant(requestToken, true, &uid, scopes)\nif err != nil {\n    panic(err)\n}\n_ = out.RedirectURL"},{"label":"Rust SDK","lang":"rust","source":"use getlocksmith::LocksmithClient;\n\nlet client = LocksmithClient::new(std::env::var(\"LOCKSMITH_API_KEY\")?, None)?;\nlet scopes_vec = vec![\n    \"openid\".to_string(),\n    \"profile\".to_string(),\n    \"email\".to_string(),\n];\nlet result = client\n    .complete_oidc_grant(\n        request_token,\n        true,\n        Some(\"user_cuid\"),\n        Some(scopes_vec.as_slice()),\n    )\n    .await?;"},{"label":"Ruby SDK","lang":"ruby","source":"require \"locksmith\"\n\nclient = Locksmith::Client.new(api_key: ENV.fetch(\"LOCKSMITH_API_KEY\"))\ndata = client.complete_oidc_grant(\n  request_token: request_token,\n  approved: true,\n  user_id: \"user_cuid\",\n  scopes: [\"openid\", \"profile\", \"email\"],\n)"},{"label":"PHP SDK","lang":"php","source":"<?php\n\nuse Locksmith\\LocksmithClient;\n\n$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));\n$data = $client->completeOidcGrant(\n    $requestToken,\n    true,\n    'user_cuid',\n    ['openid', 'profile', 'email'],\n);"},{"label":"C# SDK","lang":"csharp","source":"using Locksmith;\n\nawait using var client = new LocksmithClient(Environment.GetEnvironmentVariable(\"LOCKSMITH_API_KEY\")!);\nvar data = await client.CompleteOidcGrantAsync(\n    requestToken,\n    approved: true,\n    userId: \"user_cuid\",\n    scopes: new[] { \"openid\", \"profile\", \"email\" });"},{"label":"Java SDK","lang":"java","source":"import app.locksmith.LocksmithClient;\nimport com.fasterxml.jackson.databind.node.JsonNodeFactory;\n\ntry (LocksmithClient client = new LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\"))) {\n    var scopes = JsonNodeFactory.instance.arrayNode()\n        .add(\"openid\").add(\"profile\").add(\"email\");\n    var data = client.completeOidcGrant(requestToken, true, \"user_cuid\", scopes);\n}"},{"label":"Kotlin SDK","lang":"kotlin","source":"import app.locksmith.LocksmithClient\nimport com.fasterxml.jackson.databind.node.JsonNodeFactory\n\nval client = LocksmithClient(System.getenv(\"LOCKSMITH_API_KEY\")!!)\nval scopes = JsonNodeFactory.instance.arrayNode().apply {\n    add(\"openid\"); add(\"profile\"); add(\"email\")\n}\nval data = client.completeOidcGrant(requestToken, true, userId = \"user_cuid\", scopes = scopes)"},{"label":"Dart SDK","lang":"dart","source":"import 'dart:io';\nimport 'package:locksmith_dart/locksmith_dart.dart';\n\nfinal client = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);\nfinal data = await client.completeOidcGrant(\n  requestToken: requestToken,\n  approved: true,\n  userId: 'user_cuid',\n  scopes: ['openid', 'profile', 'email'],\n);"},{"label":"Elixir SDK","lang":"elixir","source":"client = Locksmith.Client.new(System.fetch_env!(\"LOCKSMITH_API_KEY\"))\ndata =\n  Locksmith.Client.complete_oidc_grant(client, request_token, true,\n    user_id: \"user_cuid\",\n    scopes: [\"openid\", \"profile\", \"email\"]\n  )"},{"label":"Swift SDK","lang":"swift","source":"import Locksmith\n\nlet client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment[\"LOCKSMITH_API_KEY\"]!)\nlet data = try await client.completeOidcGrant(\n    requestToken: requestToken,\n    approved: true,\n    userId: \"user_cuid\",\n    scopes: [\"openid\", \"profile\", \"email\"]\n)"}],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcGrantRequest"}}}},"responses":{"200":{"description":"OK — includes `data.redirectUrl` for consent approval or denial flow.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/OidcGrantResponseData"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"description":"`invalid_api_key` or `invalid_token` (bad request_token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"`user_banned` or `feature_not_available`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"`user_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oidc/{projectId}/.well-known/openid-configuration":{"get":{"tags":["OIDC"],"summary":"OIDC discovery document","description":"Standard OpenID Provider Metadata for this project. **Pro plan** only (403 when gated).","security":[],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"JSON discovery document","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Plan gate / `feature_not_available` style body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oidc/{projectId}/.well-known/jwks.json":{"get":{"tags":["OIDC"],"summary":"JWKS","description":"Public keys for verifying `id_token` JWTs for this project’s OIDC issuer.","security":[],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"JWK set","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Plan gate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oidc/{projectId}/authorize":{"get":{"tags":["OIDC"],"summary":"OIDC authorize","description":"Authorization endpoint. Valid `response_type` is `code` only. On success returns **302** to the OIDC application’s `loginUrl` with `request_token`, `scope`, and `app_name` query params.","security":[],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri"}},{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"scope","in":"query","required":false,"schema":{"type":"string"},"description":"Space-separated scopes; must include `openid`."},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"nonce","in":"query","required":false,"schema":{"type":"string"}},{"name":"code_challenge","in":"query","required":false,"schema":{"type":"string"}},{"name":"code_challenge_method","in":"query","required":false,"schema":{"type":"string","enum":["S256"]}}],"responses":{"302":{"description":"Redirect to registered login URL with `request_token`."},"400":{"description":"Plain-text OAuth-style error (no JSON envelope)."},"403":{"description":"Plan gate (JSON `ApiError`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oidc/{projectId}/token":{"post":{"tags":["OIDC"],"summary":"OIDC token endpoint","description":"OAuth 2.0 token endpoint (`grant_type=authorization_code`). Client auth via `Authorization: Basic` or form fields `client_id` + `client_secret`. Request body: `application/x-www-form-urlencoded` or `multipart/form-data`. Returns **OAuth-style JSON** (not `{ data: … }`) on success and on error.","security":[],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type","code","redirect_uri"],"properties":{"grant_type":{"type":"string","enum":["authorization_code"]},"code":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"code_verifier":{"type":"string","description":"Required when PKCE was used at authorize."}}}}}},"responses":{"200":{"description":"Token response (`access_token`, `id_token`, `token_type`, `expires_in`, …).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"`{ error, error_description }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcOAuthStyleError"}}}},"401":{"description":"`invalid_client`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcOAuthStyleError"}}}}}}},"/api/oidc/{projectId}/userinfo":{"get":{"tags":["OIDC"],"summary":"OIDC UserInfo","description":"`Authorization: Bearer` with the access token from the token endpoint. Returns JSON claims; failures use OAuth-style `{ error, error_description }`.","security":[{"BearerAuth":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"UserInfo claims","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcOAuthStyleError"}}},"description":"Invalid or missing bearer token"},"403":{"description":"Plan gate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}}}