{"openapi":"3.1.0","info":{"title":"Jam Public API","description":"OAuth 2.1 + identity surface for api.jam.dev.","version":"1.0.0"},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"OAuth 2.1 access token or Personal Access Token."}},"schemas":{}},"paths":{"/":{"get":{"summary":"Health check","tags":["Health"],"description":"Returns 200 ok when the public API surface is reachable.","responses":{"200":{"description":"Returns 'ok' on the public API hostname.","content":{"application/json":{"schema":{"type":"string","description":"Returns 'ok' on the public API hostname."}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"summary":"OAuth 2.0 Authorization Server Metadata (RFC 8414)","tags":["Discovery"],"responses":{"200":{"description":"Default Response","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"},"introspection_endpoint":{"type":"string","format":"uri"},"revocation_endpoint":{"type":"string","format":"uri"},"scopes_supported":{"type":"array","items":{"type":"string"}},"response_types_supported":{"type":"array","items":{"type":"string"}},"grant_types_supported":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"}}},"required":["issuer","authorization_endpoint","token_endpoint","registration_endpoint","introspection_endpoint","revocation_endpoint","scopes_supported","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported"],"additionalProperties":false}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"summary":"OAuth 2.0 Protected Resource Metadata (RFC 9728 / draft-ietf-oauth-resource-metadata)","tags":["Discovery"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"resource":{"type":"string","format":"uri"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"bearer_methods_supported":{"type":"array","items":{"type":"string","enum":["header"]}}},"required":["resource","authorization_servers","scopes_supported","bearer_methods_supported"],"additionalProperties":false}}}}}}},"/whoami":{"get":{"summary":"Authenticated user identity","tags":["Identity"],"description":"Returns the authenticated user and their team in a JSON:API envelope.","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"links":{"type":"object","properties":{"self":{"type":"string","enum":["/whoami"]}},"required":["self"],"additionalProperties":false},"data":{"type":"object","properties":{"type":{"type":"string","enum":["whoami"]},"id":{"type":"string"},"attributes":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"}},"required":["id","email"],"additionalProperties":false},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false}},"required":["user","team"],"additionalProperties":false}},"required":["type","id","attributes"],"additionalProperties":false}},"required":["links","data"],"additionalProperties":false}}}},"401":{"description":"RFC 6749 error response for protected-resource 401s.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token"]},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false,"description":"RFC 6749 error response for protected-resource 401s."}}}}}}},"/oauth/introspect":{"post":{"summary":"Introspect an access or refresh token (RFC 7662)","tags":["OAuth"],"description":"Returns `{ active: false }` for unknown / expired / revoked tokens. For active tokens, returns claims appropriate to the token kind (PAT vs OAuth).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"]}},"required":["token"],"additionalProperties":false}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"active":{"type":"boolean","enum":[false]}},"required":["active"],"additionalProperties":false},{"type":"object","properties":{"active":{"type":"boolean","enum":[true]},"tokenType":{"type":"string","enum":["pat"]},"user_id":{"type":"string"},"team_id":{"type":"string"},"scope":{"type":"string"},"token_type":{"type":"string","enum":["Bearer"]},"exp":{"type":"number"},"iat":{"type":"number"},"sub":{"type":"string"},"pat_id":{"type":"string"},"pat_prefix":{"type":"string"}},"required":["active","tokenType","user_id","team_id","token_type","exp","iat","sub","pat_id","pat_prefix"],"additionalProperties":false},{"type":"object","properties":{"active":{"type":"boolean","enum":[true]},"tokenType":{"type":"string","enum":["oauth"]},"user_id":{"type":"string"},"team_id":{"type":"string"},"client_id":{"type":"string"},"scope":{"type":"string"},"token_type":{"anyOf":[{"type":"string","enum":["Bearer"]},{"type":"string","enum":["refresh_token"]}]},"exp":{"type":"number"},"iat":{"type":"number"},"sub":{"type":"string"},"aud":{"type":"string"}},"required":["active","tokenType","user_id","team_id","client_id","token_type","exp","iat","sub","aud"],"additionalProperties":false}]}}}}}}},"/oauth/revoke":{"post":{"summary":"Revoke an access or refresh token (RFC 7009)","tags":["OAuth"],"description":"Always returns 200 with an empty body, regardless of whether the token existed.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"]}},"required":["token"],"additionalProperties":false}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}}}}},"/oauth/token":{"post":{"summary":"OAuth 2.1 token endpoint","tags":["OAuth"],"description":"Exchanges an authorization code (with PKCE) for an access + refresh token, or rotates a refresh token. Accepts client credentials in the form body (`client_secret_post` / `none`) or in the Authorization header (Bearer registration token, or Basic auth).","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"grant_type":{"type":"string","enum":["authorization_code"]},"client_id":{"type":"string"},"code":{"type":"string","minLength":1},"redirect_uri":{"type":"string","format":"uri"},"code_verifier":{"type":"string","minLength":43,"maxLength":128}},"required":["grant_type","code","redirect_uri","code_verifier"],"additionalProperties":false},{"type":"object","properties":{"grant_type":{"type":"string","enum":["refresh_token"]},"client_id":{"type":"string"},"refresh_token":{"type":"string","minLength":1},"scope":{"type":"string","nullable":true}},"required":["grant_type","refresh_token"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"number"},"refresh_token":{"type":"string"},"scope":{"type":"string"}},"required":["access_token","token_type","expires_in"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}}},"/oauth/authorize":{"get":{"summary":"OAuth 2.1 authorization endpoint (initial GET)","tags":["OAuth"],"description":"Validates the authorization request, stores it in Redis under the PKCE code_challenge, and redirects to the dashboard for user consent.","parameters":[{"schema":{"type":"string"},"in":"query","name":"client_id","required":false},{"schema":{"type":"string"},"in":"query","name":"redirect_uri","required":false},{"schema":{"type":"string"},"in":"query","name":"scope","required":false},{"schema":{"type":"string"},"in":"query","name":"code_challenge","required":false},{"schema":{"type":"string"},"in":"query","name":"code_challenge_method","required":false},{"schema":{"type":"string"},"in":"query","name":"response_type","required":false},{"schema":{"type":"string"},"in":"query","name":"state","required":false}],"responses":{"302":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"state":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"state":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}},"post":{"summary":"OAuth 2.1 authorization endpoint (consent POST)","tags":["OAuth"],"description":"Receives the user/team selection from the dashboard, validates the session cookie, mints an authorization code, and redirects to the client's redirect_uri.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"response_type":{"type":"string","enum":["code"]},"client_id":{"type":"string","minLength":1},"team_id":{"type":"string","minLength":1},"redirect_uri":{"type":"string","format":"uri"},"scope":{"type":"string","nullable":true},"state":{"type":"string","minLength":1},"code_challenge":{"type":"string","minLength":43,"maxLength":128},"code_challenge_method":{"type":"string","enum":["S256"]}},"required":["response_type","client_id","team_id","redirect_uri","code_challenge","code_challenge_method"],"additionalProperties":false}}},"required":true},"responses":{"302":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"state":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"state":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"state":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"state":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}}},"/oauth/register":{"post":{"summary":"Dynamic Client Registration (RFC 7591)","tags":["OAuth"],"description":"Register a new OAuth client. Returns client_id, registration_access_token (also exposed as client_secret), and the registered metadata.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string","minLength":1,"maxLength":255},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1},"scope":{"type":"string","nullable":true},"application_type":{"type":"string","enum":["web","native"],"default":"web"},"client_uri":{"type":"string","format":"uri","nullable":true},"logo_uri":{"type":"string","format":"uri","nullable":true},"tos_uri":{"type":"string","format":"uri","nullable":true},"policy_uri":{"type":"string","format":"uri","nullable":true},"contacts":{"type":"array","items":{"type":"string","format":"email"},"nullable":true},"software_id":{"type":"string","maxLength":255,"nullable":true},"software_version":{"type":"string","maxLength":100,"nullable":true}},"required":["client_name","redirect_uris"],"additionalProperties":false}}},"required":true},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"application_type":{"type":"string"},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"},"nullable":true},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"client_id_issued_at":{"type":"number"},"registration_access_token":{"type":"string"},"client_secret":{"type":"string"},"registration_client_uri":{"type":"string","nullable":true}},"required":["client_id","client_name","redirect_uris","application_type","client_id_issued_at","registration_access_token","client_secret"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}}},"/oauth/register/{client_id}":{"get":{"summary":"Client Configuration: GET (RFC 7592)","tags":["OAuth"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"client_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"application_type":{"type":"string"},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"},"nullable":true},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"client_id_issued_at":{"type":"number"}},"required":["client_id","client_name","redirect_uris","application_type","client_id_issued_at"],"additionalProperties":false}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}},"put":{"summary":"Client Configuration: PUT (RFC 7592)","tags":["OAuth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string","minLength":1,"maxLength":255},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1},"scope":{"type":"string","nullable":true},"application_type":{"type":"string","enum":["web","native"],"default":"web"},"client_uri":{"type":"string","format":"uri","nullable":true},"logo_uri":{"type":"string","format":"uri","nullable":true},"tos_uri":{"type":"string","format":"uri","nullable":true},"policy_uri":{"type":"string","format":"uri","nullable":true},"contacts":{"type":"array","items":{"type":"string","format":"email"},"nullable":true},"software_id":{"type":"string","maxLength":255,"nullable":true},"software_version":{"type":"string","maxLength":100,"nullable":true}},"required":["client_name","redirect_uris"],"additionalProperties":false}}},"required":true},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"client_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"application_type":{"type":"string"},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"},"nullable":true},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"client_id_issued_at":{"type":"number"},"registration_access_token":{"type":"string"},"client_secret":{"type":"string"},"registration_client_uri":{"type":"string","nullable":true}},"required":["client_id","client_name","redirect_uris","application_type","client_id_issued_at","registration_access_token","client_secret"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}},"delete":{"summary":"Client Configuration: DELETE (RFC 7592)","tags":["OAuth"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"client_id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error","error_description"],"additionalProperties":false}}}}}}},"/openapi.json":{"get":{"responses":{"200":{"description":"Default Response"}}}}},"servers":[{"url":"https://api.jam.dev"}]}