{"openapi":"3.1.0","info":{"title":"marve-py-identity-service","version":"0.1.0"},"paths":{"/accounts/health":{"get":{"tags":["Accounts"],"summary":"Health check","operationId":"health_accounts_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/accounts/jwks":{"get":{"tags":["Accounts"],"summary":"Public signing keys","description":"The JSON Web Key Set that verifies this service's ES256 tokens.","operationId":"jwks_accounts_jwks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Jwks"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/accounts/token/validate":{"get":{"tags":["Accounts"],"summary":"Check an access token","description":"The token is read from `Authorization: Bearer`. 401 unless it is a live session's token.","operationId":"validate_token_accounts_token_validate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenValidity"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"security":[{"HTTPBearer":[]}]}},"/accounts/token/refresh":{"post":{"tags":["Accounts"],"summary":"Swap a refresh token for a new token set","description":"Send the REFRESH token (not the access token) as `Authorization: Bearer`.","operationId":"refresh_token_accounts_token_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenSet"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"security":[{"HTTPBearer":[]}]}},"/accounts/login":{"post":{"tags":["Accounts"],"summary":"Sign in","description":"Returns the access, refresh and id tokens. Send exactly the fields shown.","operationId":"login_accounts_login_post","requestBody":{"content":{"application/json":{"schema":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"}},"type":"object","required":["username","password"],"title":"LoginBody","description":"LoginDto."},"example":{"username":"jane@example.com","password":"Passw0rd!"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenSet"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/accounts/login/status":{"get":{"tags":["Accounts"],"summary":"Whose session a token belongs to","description":"The token is read from `Authorization: Bearer`. 401 if the session has ended.","operationId":"is_logged_in_accounts_login_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"security":[{"HTTPBearer":[]}]}},"/accounts/logout":{"post":{"tags":["Accounts"],"summary":"Sign out","description":"Ends the session the Bearer token belongs to.","operationId":"logout_accounts_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"security":[{"HTTPBearer":[]}]}},"/accounts/password/change":{"put":{"tags":["Accounts"],"summary":"Change the signed-in user's password","description":"Needs the current password. Send exactly the fields shown.","operationId":"change_password_accounts_password_change_put","requestBody":{"content":{"application/json":{"schema":{"properties":{"old_password":{"type":"string","title":"Old Password"},"new_password":{"type":"string","title":"New Password","description":"6 to 64 characters, at most 72 bytes, with an uppercase letter, a lowercase letter, a number and a special character."},"confirm_new_password":{"type":"string","title":"Confirm New Password"}},"type":"object","required":["old_password","new_password","confirm_new_password"],"title":"ChangePasswordBody","description":"ChangePasswordDto."},"example":{"old_password":"Passw0rd!","new_password":"N3wPassw0rd!","confirm_new_password":"N3wPassw0rd!"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The passwords given do not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"security":[{"HTTPBearer":[]}]}},"/accounts/password/reset":{"post":{"tags":["Accounts"],"summary":"Start a password reset","description":"Emails a reset link to `username`, which must be an email address. The link carries a single-use token; the answer carries no account reference. 401 while the account is deactivated: only an administrator can reopen it.","operationId":"reset_password_accounts_password_reset_post","requestBody":{"content":{"application/json":{"schema":{"properties":{"username":{"type":"string","title":"Username"}},"type":"object","required":["username"],"title":"UsernameBody","description":"UsernameDto."},"example":{"username":"jane@example.com"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetStarted"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/accounts/password/{reference}":{"post":{"tags":["Accounts"],"summary":"Create the first password","description":"`reference` is the one-time code's reference from sign-up. `username` must be the email address the code was sent to. Signs the new user in. Send exactly the fields shown.","operationId":"create_password_accounts_password__reference__post","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenSet"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The account already has a password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The passwords given do not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"CreatePasswordDto. `username` is the email address the one-time code was sent to.","properties":{"username":{"title":"Username","type":"string"},"password":{"description":"6 to 64 characters, at most 72 bytes, with an uppercase letter, a lowercase letter, a number and a special character.","title":"Password","type":"string"},"confirm_password":{"title":"Confirm Password","type":"string"}},"required":["username","password","confirm_password"],"title":"CreatePasswordBody","type":"object"},"example":{"username":"jane@example.com","password":"Passw0rd!","confirm_password":"Passw0rd!"}}}}}},"/accounts/password/{reference}/set":{"put":{"tags":["Accounts"],"summary":"Set a new password after a reset","description":"`reference` is the single-use token from the reset link (not an account reference). It works once. Send exactly the fields shown. 401 while the account is deactivated.","operationId":"set_password_accounts_password__reference__set_put","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The passwords given do not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"SetPasswordDto.","properties":{"password":{"description":"6 to 64 characters, at most 72 bytes, with an uppercase letter, a lowercase letter, a number and a special character.","title":"Password","type":"string"},"confirm_password":{"title":"Confirm Password","type":"string"}},"required":["password","confirm_password"],"title":"SetPasswordBody","type":"object"},"example":{"password":"Passw0rd!","confirm_password":"Passw0rd!"}}}}}},"/accounts/{reference}/deactivate":{"post":{"tags":["Accounts"],"summary":"Deactivate an account","description":"For administrators: a caller with the `user` role gets 400, because the path always names an account, and `organisation_admin` gets 403 on anybody but themselves. `reason` must be at least 10 characters. Blocks login, ends any open session and keeps the seat: remove them from the product or the organisation to free it.","operationId":"deactivate_account_accounts__reference__deactivate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Signed in, but this role may not do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Deactivate and reactivate read only `reason`: at least 10 characters.","properties":{"reason":{"title":"Reason","type":"string"}},"required":["reason"],"title":"ReasonBody","type":"object"},"example":{"reason":"Leaving the organisation"}}}}}},"/accounts/{reference}/role":{"put":{"tags":["Accounts"],"summary":"Change an account's role","operationId":"change_role_accounts__reference__role_put","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"RoleDto.","properties":{"role":{"title":"Role","type":"string"}},"required":["role"],"title":"RoleBody","type":"object"},"example":{"role":"admin"}}}}}},"/accounts/{reference}/reactivate":{"post":{"tags":["Accounts"],"summary":"Reactivate an account","description":"For administrators: a caller with the `user` role gets 400, because the path always names an account, and `organisation_admin` gets 403. `reason` must be at least 10 characters.","operationId":"reactivate_account_accounts__reference__reactivate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceWithMessage"}}}},"400":{"description":"The body failed validation, or the request is not allowed as sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"No token, an expired or unknown token, or wrong credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Signed in, but this role may not do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Deactivate and reactivate read only `reason`: at least 10 characters.","properties":{"reason":{"title":"Reason","type":"string"}},"required":["reason"],"title":"ReasonBody","type":"object"},"example":{"reason":"Rejoined the organisation"}}}}}}},"components":{"schemas":{"ErrorEnvelope":{"properties":{"error_reference":{"type":"string","title":"Error Reference"},"error_type":{"type":"string","title":"Error Type"},"errors":{"type":"object","title":"Errors"},"status_code":{"type":"integer","title":"Status Code"},"timestamp":{"type":"string","title":"Timestamp"}},"additionalProperties":true,"type":"object","required":["error_reference","error_type","errors","status_code","timestamp"],"title":"ErrorEnvelope","description":"Every error from this service, whatever the cause. Rendered by InfrastructureError.","example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"UNAUTHORIZED_ERROR","errors":{"message":["Invalid authorization"]},"status_code":401,"timestamp":"2026-09-30T10:00:00.000000"}},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Health":{"properties":{"message":{"type":"string","title":"Message"}},"additionalProperties":true,"type":"object","required":["message"],"title":"Health","description":"GET /accounts/health.","example":{"message":"marve-py-identity-service is okay and running, fast!"}},"Jwk":{"properties":{"kid":{"type":"string","title":"Kid"},"kty":{"type":"string","title":"Kty"},"use":{"type":"string","title":"Use"},"alg":{"type":"string","title":"Alg"},"crv":{"type":"string","title":"Crv"},"x":{"type":"string","title":"X"},"y":{"type":"string","title":"Y"}},"additionalProperties":true,"type":"object","required":["kid","kty","use","alg","crv","x","y"],"title":"Jwk","description":"One public signing key."},"Jwks":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/Jwk"},"type":"array","title":"Keys"}},"additionalProperties":true,"type":"object","required":["keys"],"title":"Jwks","description":"GET /accounts/jwks: the public keys that verify this service's tokens.","example":{"keys":[{"alg":"ES256","crv":"P-256","kid":"<key id>","kty":"EC","use":"sig","x":"<x coordinate, base64url>","y":"<y coordinate, base64url>"}]}},"Reference":{"properties":{"reference":{"type":"string","title":"Reference"}},"additionalProperties":true,"type":"object","required":["reference"],"title":"Reference","description":"The reference of the account the request acted on.","example":{"reference":"11111111-1111-1111-1111-111111111111"}},"ReferenceWithMessage":{"properties":{"reference":{"type":"string","title":"Reference"},"message":{"type":"string","title":"Message"}},"additionalProperties":true,"type":"object","required":["reference","message"],"title":"ReferenceWithMessage","description":"The account's reference and what was started or done.","example":{"message":"Account reactivation initiated successfully","reference":"11111111-1111-1111-1111-111111111111"}},"ResetStarted":{"properties":{"message":{"type":"string","title":"Message"}},"additionalProperties":true,"type":"object","required":["message"],"title":"ResetStarted","description":"POST /accounts/password/reset. No account reference: the endpoint is anonymous.","example":{"message":"Password reset initiated successfully"}},"TokenSet":{"properties":{"reference":{"type":"string","title":"Reference"},"access_token":{"type":"string","title":"Access Token"},"refresh_token":{"type":"string","title":"Refresh Token"},"id_token":{"type":"string","title":"Id Token"},"role":{"type":"string","title":"Role"},"email":{"type":"string","title":"Email"}},"additionalProperties":true,"type":"object","required":["reference","access_token","refresh_token","id_token","role","email"],"title":"TokenSet","description":"Sign-in, first password and refresh: Token.to_dict.","example":{"access_token":"<access token>","email":"jane@example.com","id_token":"<id token>","reference":"11111111-1111-1111-1111-111111111111","refresh_token":"<refresh token>","role":"user"}},"TokenValidity":{"properties":{"is_valid":{"type":"boolean","title":"Is Valid"}},"additionalProperties":true,"type":"object","required":["is_valid"],"title":"TokenValidity","description":"GET /accounts/token/validate.","example":{"is_valid":true}},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","description":"The access token from sign-in, or the refresh token for /accounts/token/refresh.","scheme":"bearer"}}}}