{"openapi":"3.1.0","info":{"title":"Whitelabel API","version":"0.0.0","description":"Cloudflare starter with a local-first authenticated workspace and server-rendered public landing pages.\n\nReads need an access token with the `read` scope, writes the `write` scope, and routes that manage the account itself accept only the browser session. Tokens come from the OAuth 2.1 authorization server described in `components.securitySchemes`; client registration is open and immediate.\n\nVersioning is by date. The current version is 2026-09-10; send it as `API-Version` to pin a client, omit it for the current version. Every response under /api carries `API-Version` naming the version that answered; an unknown version is refused with 400 `unsupported_version`.\n\nRate limits are per 60 seconds: 1200 requests per address for the API, with tighter buckets on the OAuth endpoints. Every response names the policy it passed under in `RateLimit-Policy`; a refusal is 429 with `Retry-After`.\n\nEvery error is the `ApiError` shape: an `error` code or sentence, and `error_description` when there is more to say. A request without a valid token answers 401 with a `WWW-Authenticate` header naming the protected resource metadata. Developer documentation: https://whitelabel-stage.krasnoperov.me/developers.","contact":{"name":"Aleksei Krasnoperov","url":"https://krasnoperov.me"}},"servers":[{"url":"https://whitelabel-stage.krasnoperov.me","description":"Whitelabel"}],"externalDocs":{"url":"https://whitelabel-stage.krasnoperov.me/developers","description":"Developer portal"},"tags":[{"name":"Service","description":"The API index and health checks. Nothing here needs a token."},{"name":"Auth","description":"Browser sign-in with Google, sessions, and the apps a person has connected."},{"name":"OAuth","description":"The OAuth 2.1 authorization server: discovery, client registration, consent and tokens."},{"name":"User","description":"The signed-in person's profile."},{"name":"Objects","description":"Files a signed-in person uploads, and the background processing of them."}],"components":{"schemas":{"ApiError":{"type":"object","properties":{"error":{"type":"string","description":"A short code or sentence naming what went wrong"},"error_description":{"type":"string","description":"More for a person, when the code alone would not do"}},"required":["error"]}},"parameters":{"ApiVersion":{"name":"API-Version","in":"header","required":false,"description":"The API version the client was written against. Omit it for the current version, 2026-09-10. The response carries the same header, naming the version that answered.","schema":{"type":"string","enum":["2026-09-10"]}}},"securitySchemes":{"oauth2":{"type":"oauth2","description":"OAuth 2.1 authorization code with PKCE (S256), issued by this deployment. Register a client at the registration endpoint or present a Client ID Metadata Document, then send the person to authorize. Metadata: /.well-known/oauth-authorization-server.","flows":{"authorizationCode":{"authorizationUrl":"https://whitelabel-stage.krasnoperov.me/api/oauth/authorize","tokenUrl":"https://whitelabel-stage.krasnoperov.me/api/oauth/token","refreshUrl":"https://whitelabel-stage.krasnoperov.me/api/oauth/token","scopes":{"openid":"Confirm who you are","profile":"See your name","email":"See your email address","read":"Read your data","write":"Change your data on your behalf"}}}},"session":{"type":"apiKey","in":"cookie","name":"auth_token","description":"The browser session set by signing in with Google. Routes that manage the account itself accept only this, never an app token."},"registrationToken":{"type":"http","scheme":"bearer","description":"The registration access token returned by client registration (RFC 7592). It manages that one registration and nothing else."}}},"paths":{"/api":{"get":{"operationId":"apiIndex","summary":"API index","description":"Where this deployment's OpenAPI document, developer documentation, authorization metadata, MCP endpoint and health check are, as absolute URLs. Needs no token.","tags":["Service"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"description":{"type":"string"},"openapi":{"type":"string","format":"uri"},"documentation":{"type":"string","format":"uri"},"health":{"type":"string","format":"uri"},"mcp":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri"},"serverCard":{"type":"string","format":"uri"}},"required":["endpoint","serverCard"]},"authorization":{"type":"object","properties":{"authorizationServer":{"type":"string","format":"uri"},"protectedResource":{"type":"string","format":"uri"}},"required":["authorizationServer","protectedResource"]},"catalog":{"type":"string","format":"uri"},"llms":{"type":"string","format":"uri"}},"required":["name","version","description","openapi","documentation","health","mcp","authorization","catalog","llms"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/health":{"get":{"operationId":"healthCheck","summary":"Health check","description":"Reports that the service is reachable and which environment answered. Needs no token.","tags":["Service"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"environment":{"type":"string"}},"required":["status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/hello":{"get":{"operationId":"hello","summary":"Hello","description":"A fixed greeting that proves the API foundation is wired. Needs no token.","tags":["Service"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/.well-known/openid-configuration":{"get":{"operationId":"oauthDiscovery","summary":"OpenID discovery","description":"The authorization server metadata under its OpenID name. The same document as /.well-known/oauth-authorization-server.","tags":["OAuth"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"issuer":{"type":"string","format":"uri"},"authorization_endpoint":{"type":"string","format":"uri"},"token_endpoint":{"type":"string","format":"uri"},"registration_endpoint":{"type":"string","format":"uri"},"revocation_endpoint":{"type":"string","format":"uri"},"jwks_uri":{"type":"string","format":"uri"},"response_types_supported":{"type":"array","items":{"type":"string"}},"grant_types_supported":{"type":"array","items":{"type":"string"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"revocation_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"client_id_metadata_document_supported":{"type":"boolean"}},"required":["issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","jwks_uri","response_types_supported","grant_types_supported","code_challenge_methods_supported","token_endpoint_auth_methods_supported","revocation_endpoint_auth_methods_supported","scopes_supported","client_id_metadata_document_supported"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"oauthAuthorizationServerMetadata","summary":"Authorization server metadata","description":"RFC 8414: the endpoints, grant types, PKCE methods and scopes of this issuer.","tags":["OAuth"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"issuer":{"type":"string","format":"uri"},"authorization_endpoint":{"type":"string","format":"uri"},"token_endpoint":{"type":"string","format":"uri"},"registration_endpoint":{"type":"string","format":"uri"},"revocation_endpoint":{"type":"string","format":"uri"},"jwks_uri":{"type":"string","format":"uri"},"response_types_supported":{"type":"array","items":{"type":"string"}},"grant_types_supported":{"type":"array","items":{"type":"string"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"revocation_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"client_id_metadata_document_supported":{"type":"boolean"}},"required":["issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","jwks_uri","response_types_supported","grant_types_supported","code_challenge_methods_supported","token_endpoint_auth_methods_supported","revocation_endpoint_auth_methods_supported","scopes_supported","client_id_metadata_document_supported"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"oauthProtectedResource","summary":"Protected resource metadata","description":"RFC 9728: which authorization server protects the API, and which scopes it understands.","tags":["OAuth"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"resource":{"type":"string","format":"uri"},"resource_name":{"type":"string"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"bearer_methods_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"jwks_uri":{"type":"string","format":"uri"},"resource_documentation":{"type":"string","format":"uri"}},"required":["resource","resource_name","authorization_servers","bearer_methods_supported","scopes_supported","jwks_uri","resource_documentation"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"operationId":"oauthProtectedResourceMcp","summary":"MCP protected resource metadata","description":"RFC 9728 for the MCP endpoint, which is its own resource. A client refused by /mcp is pointed here to find the authorization server.","tags":["OAuth"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"resource":{"type":"string","format":"uri"},"resource_name":{"type":"string"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"bearer_methods_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"jwks_uri":{"type":"string","format":"uri"},"resource_documentation":{"type":"string","format":"uri"}},"required":["resource","resource_name","authorization_servers","bearer_methods_supported","scopes_supported","jwks_uri","resource_documentation"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/.well-known/jwks.json":{"get":{"operationId":"oauthJwks","summary":"Signing keys","description":"The JSON Web Key Set that verifies the tokens this issuer signs.","tags":["OAuth"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"kid":{"type":"string"},"alg":{"type":"string"},"use":{"type":"string"}},"required":["kid","alg","use"],"additionalProperties":{}}}},"required":["keys"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oauth/authorize":{"get":{"operationId":"oauthAuthorize","summary":"Authorization endpoint","description":"Starts an authorization code flow with PKCE (S256). Sends the person to sign in and consent, then back to the redirect URI with a code.","tags":["OAuth"],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"client_id","in":"query"},{"schema":{"type":"string","format":"uri"},"required":true,"name":"redirect_uri","in":"query"},{"schema":{"type":"string","enum":["code"]},"required":true,"name":"response_type","in":"query"},{"schema":{"type":"string","minLength":43,"maxLength":128},"required":true,"name":"code_challenge","in":"query"},{"schema":{"type":"string","enum":["S256"]},"required":true,"name":"code_challenge_method","in":"query"},{"schema":{"type":"string"},"required":false,"name":"scope","in":"query"},{"schema":{"type":"string","format":"uri"},"required":false,"name":"resource","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"302":{"description":"Sends the browser to the next step of the flow; there is no body","headers":{"Location":{"description":"Where the browser goes next","schema":{"type":"string","format":"uri"}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"unauthorized_client: the client_id is not registered here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oauth/callback":{"get":{"operationId":"oauthCallback","summary":"Sign-in return during authorization","description":"Where Google returns the browser while a client is being authorized. Not called by clients themselves.","tags":["OAuth"],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"code","in":"query"},{"schema":{"type":"string","minLength":1},"required":true,"name":"state","in":"query"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"302":{"description":"Sends the browser to the next step of the flow; there is no body","headers":{"Location":{"description":"Where the browser goes next","schema":{"type":"string","format":"uri"}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oauth/token":{"post":{"operationId":"oauthToken","summary":"Token endpoint","description":"Exchanges an authorization code, or a refresh token, for tokens. Form-encoded as RFC 6749 requires; JSON is accepted too.","tags":["OAuth"],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"oneOf":[{"type":"object","properties":{"grant_type":{"type":"string","enum":["authorization_code"]},"code":{"type":"string","minLength":1},"code_verifier":{"type":"string","minLength":43,"maxLength":128},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"resource":{"type":"string","format":"uri"}},"required":["grant_type","code","code_verifier","redirect_uri"]},{"type":"object","properties":{"grant_type":{"type":"string","enum":["refresh_token"]},"refresh_token":{"type":"string","minLength":1},"scope":{"type":"string"},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"resource":{"type":"string","format":"uri"}},"required":["grant_type","refresh_token"]}]}},"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"grant_type":{"type":"string","enum":["authorization_code"]},"code":{"type":"string","minLength":1},"code_verifier":{"type":"string","minLength":43,"maxLength":128},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"resource":{"type":"string","format":"uri"}},"required":["grant_type","code","code_verifier","redirect_uri"]},{"type":"object","properties":{"grant_type":{"type":"string","enum":["refresh_token"]},"refresh_token":{"type":"string","minLength":1},"scope":{"type":"string"},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"resource":{"type":"string","format":"uri"}},"required":["grant_type","refresh_token"]}]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_in":{"type":"number"},"scope":{"type":"string"},"refresh_token":{"type":"string"},"refresh_token_expires_in":{"type":"number"},"user":{"type":["object","null"],"properties":{"id":{"type":"number"},"email":{"type":"string","format":"email"},"name":{"type":"string"}},"required":["id"]}},"required":["access_token","token_type","expires_in","scope","user"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"invalid_client: the client credentials were missing or wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/oauth/revoke":{"post":{"operationId":"oauthRevoke","summary":"Revoke a token","description":"RFC 7009. Revoking a refresh token ends its grant; the client must authorize again. Answers 200 with an empty object whether or not the token was known, as the RFC requires.","tags":["OAuth"],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"]},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1}},"required":["token"]}},"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"]},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1}},"required":["token"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"invalid_client: the client credentials were missing or wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/oauth/authorize/request":{"get":{"operationId":"oauthApprovalRequest","summary":"Pending consent request","description":"What the consent page shows: the client and the scopes awaiting the signed-in person's decision. Browser session only.","tags":["OAuth"],"security":[{"session":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"request","in":"query"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string"},"clientName":{"type":"string"},"redirectUri":{"type":"string","format":"uri"},"scopes":{"type":"array","items":{"type":"string"}},"user":{"type":"object","properties":{"id":{"type":"number"},"email":{"type":"string","format":"email"}},"required":["id","email"]}},"required":["clientId","clientName","redirectUri","scopes","user"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/oauth/authorize/decision":{"post":{"operationId":"oauthApprovalDecision","summary":"Decide a consent request","description":"Approves or declines a pending request. Approval issues the authorization code. Browser session only.","tags":["OAuth"],"security":[{"session":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","minLength":1},"approved":{"type":"boolean"}},"required":["requestId","approved"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"redirectUrl":{"type":"string","format":"uri"}},"required":["redirectUrl"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/oauth/register":{"post":{"operationId":"oauthClientRegister","summary":"Register a client","description":"RFC 7591 dynamic registration. Open and immediate: the response carries the client_id and a registration access token for managing the registration.","tags":["OAuth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"token_endpoint_auth_method":{"type":"string"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"token_endpoint_auth_method":{"type":"string"},"client_id_issued_at":{"type":"number"},"client_secret_expires_at":{"type":"number"},"registration_client_uri":{"type":"string","format":"uri"},"registration_access_token":{"type":"string"},"client_secret":{"type":"string"}},"required":["client_id","client_name","redirect_uris","grant_types","response_types","scope","token_endpoint_auth_method","client_id_issued_at","client_secret_expires_at","registration_client_uri"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/oauth/register/{clientId}":{"get":{"operationId":"oauthClientGet","summary":"Read a client registration","description":"RFC 7592. The registration as it stands, for the client that holds its registration access token.","tags":["OAuth"],"security":[{"registrationToken":[]}],"parameters":[{"schema":{"type":"string","description":"clientId path parameter"},"required":true,"description":"clientId path parameter","name":"clientId","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"token_endpoint_auth_method":{"type":"string"},"client_id_issued_at":{"type":"number"},"client_secret_expires_at":{"type":"number"},"registration_client_uri":{"type":"string","format":"uri"},"registration_access_token":{"type":"string"},"client_secret":{"type":"string"}},"required":["client_id","client_name","redirect_uris","grant_types","response_types","scope","token_endpoint_auth_method","client_id_issued_at","client_secret_expires_at","registration_client_uri"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"oauthClientUpdate","summary":"Update a client registration","description":"RFC 7592. Replaces the registered metadata; the client_id stays.","tags":["OAuth"],"security":[{"registrationToken":[]}],"parameters":[{"schema":{"type":"string","description":"clientId path parameter"},"required":true,"description":"clientId path parameter","name":"clientId","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"token_endpoint_auth_method":{"type":"string"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"token_endpoint_auth_method":{"type":"string"},"client_id_issued_at":{"type":"number"},"client_secret_expires_at":{"type":"number"},"registration_client_uri":{"type":"string","format":"uri"},"registration_access_token":{"type":"string"},"client_secret":{"type":"string"}},"required":["client_id","client_name","redirect_uris","grant_types","response_types","scope","token_endpoint_auth_method","client_id_issued_at","client_secret_expires_at","registration_client_uri"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"oauthClientDelete","summary":"Delete a client registration","description":"RFC 7592. Removes the registration; tokens issued to the client stop working.","tags":["OAuth"],"security":[{"registrationToken":[]}],"parameters":[{"schema":{"type":"string","description":"clientId path parameter"},"required":true,"description":"clientId path parameter","name":"clientId","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"204":{"description":"Done; there is no body"},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/session":{"get":{"operationId":"authSession","summary":"Current session","description":"The signed-in user of the presented session cookie or bearer token, or null when there is none. Never fails for an anonymous caller.","tags":["Auth"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":["object","null"],"properties":{"id":{"type":"number"},"email":{"type":"string","format":"email"},"name":{"type":"string"}},"required":["id","email","name"]},"config":{"type":"object","properties":{"googleClientId":{"type":"string"},"environment":{"type":"string"}},"required":["googleClientId"]}},"required":["user","config"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/auth/google/login":{"get":{"operationId":"authGoogleLogin","summary":"Start Google sign-in","description":"Redirects the browser to Google. The optional redirect query names the local path to return to afterwards.","tags":["Auth"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"return_to","in":"query"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"302":{"description":"Sends the browser to the next step of the flow; there is no body","headers":{"Location":{"description":"Where the browser goes next","schema":{"type":"string","format":"uri"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/google/callback":{"get":{"operationId":"authGoogleCallback","summary":"Finish Google sign-in","description":"Where Google sends the browser back. Creates the account on first sign-in, starts a session and redirects to the requested page.","tags":["Auth"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"code","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error","in":"query"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"302":{"description":"Sends the browser to the next step of the flow; there is no body","headers":{"Location":{"description":"Where the browser goes next","schema":{"type":"string","format":"uri"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/auth/logout":{"post":{"operationId":"authLogout","summary":"Sign out","description":"Ends the current browser session and clears its cookie.","tags":["Auth"],"security":[{"session":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/auth/logout/all":{"post":{"operationId":"authLogoutAll","summary":"Sign out everywhere","description":"Ends every session of the signed-in user. Accepts a browser session only, never an app token.","tags":["Auth"],"security":[{"session":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"number"}},"required":["revoked"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/auth/grants":{"get":{"operationId":"authGrantsList","summary":"List connected apps","description":"The OAuth grants the signed-in user has given: the client, the scopes, and when each was created and last used. Browser session only.","tags":["Auth"],"security":[{"session":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"grants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string"},"clientName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"lastUsedAt":{"type":"string"},"expiresAt":{"type":"string"},"revokedAt":{"type":["string","null"]}},"required":["id","clientId","clientName","scopes","createdAt","lastUsedAt","expiresAt","revokedAt"]}}},"required":["grants"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]},"delete":{"operationId":"authGrantsRevokeAll","summary":"Revoke all connected apps","description":"Revokes every grant of the signed-in user. Browser session only.","tags":["Auth"],"security":[{"session":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"number"}},"required":["revoked"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/auth/grants/{grantId}":{"delete":{"operationId":"authGrantRevoke","summary":"Revoke a connected app","description":"Revokes one grant. Every token issued under it stops working on its next use. Browser session only.","tags":["Auth"],"security":[{"session":[]}],"parameters":[{"schema":{"type":"string","description":"grantId path parameter"},"required":true,"description":"grantId path parameter","name":"grantId","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"number"}},"required":["revoked"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/user/profile":{"get":{"operationId":"userProfileGet","summary":"Get profile","description":"The signed-in user: id, email and display name.","tags":["User"],"security":[{"oauth2":["read"]},{"session":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"number"},"email":{"type":"string","format":"email"},"name":{"type":"string"}},"required":["id","email","name"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]},"patch":{"operationId":"userProfileUpdate","summary":"Update profile","description":"Changes the display name of the signed-in user.","tags":["User"],"security":[{"oauth2":["write"]},{"session":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"user":{"type":"object","properties":{"id":{"type":"number"},"email":{"type":"string","format":"email"},"name":{"type":"string"}},"required":["id","email","name"]}},"required":["success","user"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/uploads/{name}":{"put":{"operationId":"uploadPut","summary":"Upload a file","description":"Stores the request body under the signed-in user's own namespace. Content-Length is required, and uploads above 25 MB are refused.","tags":["Objects"],"security":[{"oauth2":["write"]},{"session":[]}],"parameters":[{"schema":{"type":"string","description":"name path parameter"},"required":true,"description":"name path parameter","name":"name","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"size":{"type":"number"},"contentType":{"type":"string"}},"required":["key","name","size","contentType"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"get":{"operationId":"uploadGet","summary":"Download a file","description":"Serves a file the signed-in user uploaded, sandboxed so it can never run as a page of this site.","tags":["Objects"],"security":[{"oauth2":["read"]},{"session":[]}],"parameters":[{"schema":{"type":"string","description":"name path parameter"},"required":true,"description":"name path parameter","name":"name","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"The stored bytes, with the Content-Type they were uploaded under","content":{"*/*":{"schema":{"type":"string","format":"binary"}}}},"206":{"description":"The bytes a Range header asked for","content":{"*/*":{"schema":{"type":"string","format":"binary"}}}},"304":{"description":"Unchanged since the ETag or date the request named; there is no body"},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/objects/ingest":{"post":{"operationId":"objectIngestStart","summary":"Start processing an object","description":"Starts the background workflow for one of the signed-in user's objects and returns the instance to poll.","tags":["Objects"],"security":[{"oauth2":["write"]},{"session":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1,"description":"Key of a stored object belonging to the caller"}},"required":["key"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"instanceId":{"type":"string"},"status":{"type":"string"}},"required":["instanceId","status"]}}}},"400":{"description":"The request did not match the schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"$ref":"#/components/parameters/ApiVersion"}]}},"/api/objects/ingest/{instanceId}":{"get":{"operationId":"objectIngestStatus","summary":"Processing status","description":"The status of a workflow instance the signed-in user started.","tags":["Objects"],"security":[{"oauth2":["read"]},{"session":[]}],"parameters":[{"schema":{"type":"string","description":"instanceId path parameter"},"required":true,"description":"instanceId path parameter","name":"instanceId","in":"path"},{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"instanceId":{"type":"string"},"status":{"type":"string"}},"required":["instanceId","status"]}}}},"401":{"description":"No valid credentials. WWW-Authenticate names the protected resource metadata to start from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The credentials are valid but not enough: the token lacks the scope, or the action needs a browser session rather than an app token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited. RateLimit names the policy; Retry-After says when to try again","headers":{"Retry-After":{"description":"Seconds until the bucket refills","schema":{"type":"integer"}},"RateLimit":{"description":"The policy, with nothing left in it","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"webhooks":{}}