{"openapi":"3.1.0","info":{"title":"XRHealth Platform API","version":"1.0.0","description":"Common API for XRHealth applications and approved integrations."},"servers":[{"url":"/v1"}],"paths":{"/":{"get":{"operationId":"getApiMetadata","summary":"Get API metadata","responses":{"200":{"description":"API metadata"}}}},"/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"Get the OpenAPI document","responses":{"200":{"description":"OpenAPI document"}}}},"/auth/passwordless/start":{"post":{"operationId":"startPatientPasswordlessLogin","summary":"Send a one-time patient login code","security":[{"applicationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordlessStartRequest"}}}},"responses":{"202":{"description":"A code request was accepted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/passwordless/verify":{"post":{"operationId":"verifyPatientPasswordlessLogin","summary":"Exchange a one-time code for XRHealth tokens","security":[{"applicationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordlessVerifyRequest"}}}},"responses":{"200":{"description":"XRHealth access and refresh tokens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/public/passwordless/start":{"post":{"operationId":"startPublicPatientPasswordlessLogin","summary":"Start passwordless login for a registered public client","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPasswordlessStartRequest"}}}},"responses":{"202":{"description":"A code request was accepted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/public/passwordless/verify":{"post":{"operationId":"verifyPublicPatientPasswordlessLogin","summary":"Exchange a passwordless code for a short-lived PKCE authorization code","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPasswordlessVerifyRequest"}}}},"responses":{"200":{"description":"A one-time authorization code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationCodeResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/public/token":{"post":{"operationId":"exchangePublicPatientToken","summary":"Exchange a PKCE authorization code or rotate a public-client refresh token","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTokenRequest"}}}},"responses":{"200":{"description":"XRHealth access and refresh tokens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/public/token/revoke":{"post":{"operationId":"revokePublicPatientToken","summary":"Revoke a public-client refresh token","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTokenRevokeRequest"}}}},"responses":{"200":{"description":"The refresh token is no longer usable."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/token/refresh":{"post":{"operationId":"refreshPatientToken","summary":"Rotate a patient refresh token","security":[{"applicationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}}},"responses":{"200":{"description":"Rotated XRHealth tokens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/auth/token/revoke":{"post":{"operationId":"revokePatientToken","summary":"Revoke a patient refresh token","security":[{"applicationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}}},"responses":{"200":{"description":"The refresh token is no longer usable."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/me":{"get":{"operationId":"getCurrentPatient","summary":"Get the current public patient subject","security":[{"applicationToken":[],"patientBearer":[]}],"responses":{"200":{"description":"The current public patient identity.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/.well-known/jwks.json":{"get":{"operationId":"getPatientApiJwks","summary":"Get the public signing keys for XRHealth patient access tokens","responses":{"200":{"description":"JSON Web Key Set.","content":{"application/json":{"schema":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"type":"object"}}}}}}},"503":{"$ref":"#/components/responses/Unavailable"}}}}},"components":{"securitySchemes":{"applicationToken":{"type":"apiKey","in":"header","name":"X-XRHealth-Application-Token"},"patientBearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"PasswordlessStartRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}},"additionalProperties":false},"PasswordlessVerifyRequest":{"type":"object","required":["request_id","email","code"],"properties":{"request_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"code":{"type":"string","pattern":"^\\d{4,8}$"}},"additionalProperties":false},"PublicPasswordlessStartRequest":{"type":"object","required":["client_id","email","code_challenge","code_challenge_method"],"properties":{"client_id":{"type":"string","minLength":8,"maxLength":128},"email":{"type":"string","format":"email"},"code_challenge":{"type":"string","minLength":43,"maxLength":128},"code_challenge_method":{"type":"string","enum":["S256"]}},"additionalProperties":false},"PublicPasswordlessVerifyRequest":{"type":"object","required":["client_id","request_id","email","code"],"properties":{"client_id":{"type":"string","minLength":8,"maxLength":128},"request_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"code":{"type":"string","pattern":"^\\d{4,8}$"}},"additionalProperties":false},"PublicTokenRequest":{"type":"object","required":["client_id","grant_type"],"properties":{"client_id":{"type":"string","minLength":8,"maxLength":128},"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"authorization_code":{"type":"string","minLength":16},"code_verifier":{"type":"string","minLength":43,"maxLength":128},"refresh_token":{"type":"string","minLength":16}},"additionalProperties":false},"PublicTokenRevokeRequest":{"type":"object","required":["client_id","refresh_token"],"properties":{"client_id":{"type":"string","minLength":8,"maxLength":128},"refresh_token":{"type":"string","minLength":16}},"additionalProperties":false},"AuthorizationCodeResponse":{"type":"object","required":["authorization_code","expires_in"],"properties":{"authorization_code":{"type":"string"},"expires_in":{"type":"integer","example":60}}},"RefreshTokenRequest":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string","minLength":16}},"additionalProperties":false},"TokenResponse":{"type":"object","required":["token_type","access_token","expires_in","refresh_token","refresh_token_expires_in","subject","scope"],"properties":{"token_type":{"type":"string","enum":["Bearer"]},"access_token":{"type":"string"},"expires_in":{"type":"integer","example":900},"refresh_token":{"type":"string"},"refresh_token_expires_in":{"type":"integer","example":2592000},"subject":{"type":"string","description":"Opaque public patient subject."},"scope":{"type":"string","example":"patient:login patient:read"}}},"MeResponse":{"type":"object","required":["subject","application","scopes"],"properties":{"subject":{"type":"string"},"application":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}}},"responses":{"BadRequest":{"description":"The request is invalid."},"Unauthorized":{"description":"The application or patient token is invalid."},"Forbidden":{"description":"The token does not have access to this resource."},"RateLimited":{"description":"The request was rate limited."},"Unavailable":{"description":"The authentication module is temporarily unavailable."}}}}