{"openapi":"3.0.3","info":{"title":"User Registry API","version":"1.0.0","description":"RESTful API for managing patients, practitioners, and organizations in the MEDrecord healthcare platform.","contact":{"name":"MEDrecord","url":"https://medrecord.co"}},"servers":[{"url":"https://user-registry.healthtalk.ai","description":"User Registry API Server"}],"tags":[{"name":"Patients","description":"Patient management endpoints"},{"name":"Practitioners","description":"Practitioner management endpoints"},{"name":"Organizations","description":"Organization management endpoints"},{"name":"Organization Members","description":"Organization membership management"},{"name":"Onboarding","description":"Practitioner onboarding endpoints"},{"name":"Billing","description":"Subscription and seat management"},{"name":"Notifications","description":"Billing and system notifications"},{"name":"Admin","description":"System administrator endpoints"},{"name":"Compatibility","description":"Backward compatibility endpoints for legacy services"}],"components":{"schemas":{"Gender":{"type":"string","enum":["MALE","FEMALE","OTHER","UNKNOWN"],"description":"Gender classification"},"PatientVisibilityMode":{"type":"string","enum":["ALL_DOCTORS","OWN_ONLY"],"description":"Patient visibility mode for organization"},"CapabilitiesResponse":{"type":"object","properties":{"canManageMembers":{"type":"boolean"},"canManageTemplates":{"type":"boolean"},"canManageSettings":{"type":"boolean"}},"required":["canManageMembers","canManageTemplates","canManageSettings"],"description":"Member capabilities in organization"},"PersonResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"givenName":{"type":"string","nullable":true},"familyName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"gender":{"type":"string","nullable":true,"enum":["MALE","FEMALE","OTHER","UNKNOWN"]}},"required":["id","givenName","familyName","email","phone","gender"],"description":"Person demographic information"},"PatientResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"personId":{"type":"string","format":"uuid"},"person":{"$ref":"#/components/schemas/PersonResponse"},"dateOfBirth":{"type":"string","nullable":true},"managingOrganizationId":{"type":"string","nullable":true,"format":"uuid"},"managedByPractitionerId":{"type":"string","nullable":true,"format":"uuid"},"externalId":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","personId","person","dateOfBirth","managingOrganizationId","managedByPractitionerId","externalId","createdAt","updatedAt"],"description":"Patient record with person demographics"},"PractitionerResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"personId":{"type":"string","nullable":true,"format":"uuid"},"person":{"allOf":[{"$ref":"#/components/schemas/PersonResponse"},{"nullable":true}]},"displayName":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","userId","personId","person","displayName","createdAt","updatedAt"],"description":"Practitioner record"},"OrganizationResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"patientVisibilityMode":{"type":"string","enum":["ALL_DOCTORS","OWN_ONLY"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","patientVisibilityMode","createdAt","updatedAt"],"description":"Organization record"},"PractitionerRoleResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"practitionerId":{"type":"string","format":"uuid"},"roleSystem":{"type":"string"},"roleCode":{"type":"string"},"roleDisplay":{"type":"string","nullable":true},"capabilities":{"$ref":"#/components/schemas/CapabilitiesResponse"}},"required":["id","organizationId","practitionerId","roleSystem","roleCode","roleDisplay","capabilities"],"description":"Practitioner role in organization"},"OrganizationMemberResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"practitioner":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"displayName":{"type":"string"},"email":{"type":"string","nullable":true}},"required":["id","displayName","email"]},"role":{"type":"object","properties":{"system":{"type":"string"},"code":{"type":"string"},"display":{"type":"string","nullable":true}},"required":["system","code","display"]},"capabilities":{"$ref":"#/components/schemas/CapabilitiesResponse"}},"required":["id","organizationId","practitioner","role","capabilities"],"description":"Organization member with practitioner details"},"OrganizationMembershipResponse":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/OrganizationResponse"},"role":{"type":"object","properties":{"system":{"type":"string"},"code":{"type":"string"},"display":{"type":"string","nullable":true}},"required":["system","code","display"]},"capabilities":{"$ref":"#/components/schemas/CapabilitiesResponse"}},"required":["organization","role","capabilities"],"description":"Practitioner's membership in organization"},"PractitionerWithMembershipsResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"personId":{"type":"string","nullable":true,"format":"uuid"},"displayName":{"type":"string"},"person":{"allOf":[{"$ref":"#/components/schemas/PersonResponse"},{"nullable":true}]},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationMembershipResponse"}},"isSystemAdmin":{"type":"boolean"}},"required":["id","personId","displayName","person","memberships","isSystemAdmin"],"description":"Practitioner with organization memberships and admin status"},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"statusCode":{"type":"integer"}},"required":["error"],"description":"Error response"},"PatientCreateRequest":{"type":"object","properties":{"person":{"type":"object","properties":{"givenName":{"type":"string","nullable":true,"maxLength":255},"familyName":{"type":"string","nullable":true,"maxLength":255},"email":{"type":"string","nullable":true,"maxLength":255,"format":"email"},"phone":{"type":"string","nullable":true,"maxLength":50},"gender":{"type":"string","nullable":true,"enum":["MALE","FEMALE","OTHER","UNKNOWN"]}}},"dateOfBirth":{"type":"string","nullable":true,"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"managingOrganizationId":{"type":"string","nullable":true,"format":"uuid"},"managedByPractitionerId":{"type":"string","nullable":true,"format":"uuid"},"externalId":{"type":"string","nullable":true,"maxLength":128}},"required":["person"]},"PatientUpdateRequest":{"type":"object","properties":{"person":{"type":"object","properties":{"givenName":{"type":"string","nullable":true,"maxLength":255},"familyName":{"type":"string","nullable":true,"maxLength":255},"email":{"type":"string","nullable":true,"maxLength":255,"format":"email"},"phone":{"type":"string","nullable":true,"maxLength":50},"gender":{"type":"string","nullable":true,"enum":["MALE","FEMALE","OTHER","UNKNOWN"]}}},"dateOfBirth":{"type":"string","nullable":true,"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"managingOrganizationId":{"type":"string","nullable":true,"format":"uuid"},"externalId":{"type":"string","nullable":true,"maxLength":128}},"required":["person"]},"OrganizationCreateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"patientVisibilityMode":{"type":"string","enum":["ALL_DOCTORS","OWN_ONLY"],"default":"OWN_ONLY"}},"required":["name"]},"OrganizationUpdateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"patientVisibilityMode":{"type":"string","enum":["ALL_DOCTORS","OWN_ONLY"]}}},"OrgMemberAddRequest":{"type":"object","properties":{"practitionerId":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"roleSystem":{"type":"string","minLength":1,"maxLength":255},"roleCode":{"type":"string","minLength":1,"maxLength":128},"roleDisplay":{"type":"string","nullable":true,"maxLength":255},"canManageMembers":{"type":"boolean","default":false},"canManageTemplates":{"type":"boolean","default":false},"canManageSettings":{"type":"boolean","default":false},"canManageBilling":{"type":"boolean","default":false}},"required":["roleSystem","roleCode"]},"OrgMemberUpdateRequest":{"type":"object","properties":{"roleSystem":{"type":"string","minLength":1,"maxLength":255},"roleCode":{"type":"string","minLength":1,"maxLength":128},"roleDisplay":{"type":"string","nullable":true,"maxLength":255},"canManageMembers":{"type":"boolean"},"canManageTemplates":{"type":"boolean"},"canManageSettings":{"type":"boolean"},"canManageBilling":{"type":"boolean"}}},"PractitionerOnboardingRequest":{"type":"object","properties":{"givenName":{"type":"string","nullable":true,"maxLength":255},"familyName":{"type":"string","nullable":true,"maxLength":255},"gender":{"type":"string","nullable":true,"enum":["MALE","FEMALE","OTHER","UNKNOWN"]},"organizationId":{"type":"string","nullable":true,"format":"uuid"},"roleCode":{"type":"string","minLength":1,"maxLength":128},"roleDisplay":{"type":"string","nullable":true,"maxLength":255},"canManageMembers":{"type":"boolean","default":false},"canManageTemplates":{"type":"boolean","default":false},"canManageSettings":{"type":"boolean","default":false},"canManageBilling":{"type":"boolean","default":false}},"required":["roleCode"]},"PersonListRequest":{"type":"object","properties":{"userIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1},"count":{"type":"integer","minimum":0,"exclusiveMinimum":true}},"required":["userIds"]},"X-User-Id":{"type":"string","format":"uuid"},"X-Organization-Id":{"type":"string","format":"uuid"},"UserRoleResponse":{"type":"object","properties":{"role":{"type":"string","enum":["DOCTOR","NONE"]}},"required":["role"]},"UserOrganizationResponse":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"organizationName":{"type":"string"},"canManageMembers":{"type":"boolean"},"canManageTemplates":{"type":"boolean"},"canManageSettings":{"type":"boolean"},"roleSystem":{"type":"string"},"roleCode":{"type":"string"},"roleDisplay":{"type":"string","nullable":true}},"required":["organizationId","organizationName","canManageMembers","canManageTemplates","canManageSettings","roleSystem","roleCode","roleDisplay"]},"BillingStatus":{"type":"object","properties":{"subscription":{"type":"object","properties":{"status":{"type":"string","nullable":true,"enum":["active","past_due","canceled","unpaid"]},"stripeSeats":{"type":"integer"},"currentPeriodEnd":{"type":"string","nullable":true,"format":"date-time"}},"required":["status","stripeSeats","currentPeriodEnd"]},"trial":{"type":"object","properties":{"active":{"type":"boolean"},"endsAt":{"type":"string","nullable":true,"format":"date-time"}},"required":["active","endsAt"]},"seats":{"type":"object","properties":{"paid":{"type":"integer"},"complimentary":{"type":"integer"},"total":{"type":"integer"},"assigned":{"type":"integer"},"available":{"type":"integer"}},"required":["paid","complimentary","total","assigned","available"]},"grace":{"type":"object","properties":{"active":{"type":"boolean"},"endsAt":{"type":"string","nullable":true,"format":"date-time"}},"required":["active","endsAt"]}},"required":["subscription","trial","seats","grace"]}},"parameters":{"X-User-Id":{"schema":{"$ref":"#/components/schemas/X-User-Id"},"required":true,"description":"Gateway-provided user ID","name":"X-User-Id","in":"header"},"X-Organization-Id":{"schema":{"$ref":"#/components/schemas/X-Organization-Id"},"required":false,"description":"Current organization context","name":"X-Organization-Id","in":"header"}}},"paths":{"/api/v1/patients":{"get":{"tags":["Patients"],"summary":"List patients","description":"List patients accessible to the current practitioner based on organization visibility mode.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"organizationId","in":"query"}],"responses":{"200":{"description":"List of patients","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientResponse"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Patients"],"summary":"Create patient","description":"Create a new patient with person demographics.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientCreateRequest"}}}},"responses":{"201":{"description":"Patient created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/patients/{id}":{"get":{"tags":["Patients"],"summary":"Get patient","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Patient details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientResponse"}}}},"404":{"description":"Patient not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["Patients"],"summary":"Update patient","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientUpdateRequest"}}}},"responses":{"200":{"description":"Patient updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Patient not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Patients"],"summary":"Delete patient","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Patient deleted"},"404":{"description":"Patient not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/patients/managed":{"get":{"tags":["Patients"],"summary":"Get managed patients","description":"Get patients managed by a specific practitioner.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"practitionerId","in":"query"}],"responses":{"200":{"description":"List of managed patients","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientResponse"}}}}}}}},"/api/v1/practitioners":{"get":{"tags":["Practitioners"],"summary":"List practitioners","parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"organizationId","in":"query"}],"responses":{"200":{"description":"List of practitioners","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PractitionerResponse"}}}}}}}},"/api/v1/practitioners/me":{"get":{"tags":["Practitioners"],"summary":"Get current practitioner with memberships","description":"Returns the practitioner record for the authenticated user with all organization memberships and admin status. Returns null if user is authenticated but not yet onboarded.","responses":{"200":{"description":"Current practitioner with memberships and admin status","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PractitionerWithMembershipsResponse"},{"nullable":true}]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/practitioners/{id}":{"get":{"tags":["Practitioners"],"summary":"Get practitioner by ID","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Practitioner details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PractitionerResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/practitioners/by-user/{userId}":{"get":{"tags":["Practitioners"],"summary":"Get practitioner by user ID","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"Practitioner details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PractitionerResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/organizations":{"get":{"tags":["Organizations"],"summary":"List organizations","description":"List organizations. System admins see all organizations. Regular practitioners see only organizations they belong to.","responses":{"200":{"description":"List of organizations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationResponse"}}}}}}},"post":{"tags":["Organizations"],"summary":"Create organization","description":"System admin only. Create a new organization (initially empty). System admin can then add members.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationCreateRequest"}}}},"responses":{"201":{"description":"Organization created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - system admin only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/organizations/{id}":{"get":{"tags":["Organizations"],"summary":"Get organization","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Organization details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["Organizations"],"summary":"Update organization","description":"Update organization settings (name, patientVisibilityMode). Requires canManageSettings capability or system admin.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationUpdateRequest"}}}},"responses":{"200":{"description":"Organization updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - requires canManageSettings capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Organizations"],"summary":"Delete organization","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Organization deleted"},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/organizations/{id}/members":{"get":{"tags":["Organization Members"],"summary":"List organization members","description":"Get all members of an organization with practitioner details. Requires org membership or system admin.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"List of members with practitioner details","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationMemberResponse"}}}}},"403":{"description":"Forbidden - not a member of this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Organization Members"],"summary":"Add organization member","description":"Add member by email or practitionerId. Requires org admin or system admin. Member is added immediately if email/practitioner exists.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMemberAddRequest"}}}},"responses":{"201":{"description":"Member added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - not org admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Email not found - user must register first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - already a member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/organizations/{id}/members/{practitionerId}":{"put":{"tags":["Organization Members"],"summary":"Update organization member","description":"Update member role and capabilities (canManageMembers, canManageTemplates, canManageSettings). Requires org admin or system admin. Cannot remove canManageMembers or canManageSettings if this is the last member with that capability.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"practitionerId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMemberUpdateRequest"}}}},"responses":{"200":{"description":"Member updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMemberResponse"}}}},"400":{"description":"Bad request - cannot remove last member with canManageMembers or canManageSettings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - not org admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Organization Members"],"summary":"Remove organization member","description":"Remove member from organization. Requires org admin or system admin. Cannot remove last member with any capability.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"practitionerId","in":"path"}],"responses":{"204":{"description":"Member removed"},"400":{"description":"Bad request - cannot remove last member with capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - not org admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/onboarding/practitioner":{"post":{"tags":["Onboarding"],"summary":"Onboard practitioner","description":"Creates practitioner record and optionally joins an organization.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PractitionerOnboardingRequest"}}}},"responses":{"201":{"description":"Practitioner onboarded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PractitionerResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Already onboarded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/onboarding/status":{"get":{"tags":["Onboarding"],"summary":"Get onboarding status","description":"Returns onboarding status for the authenticated user.","responses":{"200":{"description":"Onboarding status","content":{"application/json":{"schema":{"type":"object","properties":{"onboarded":{"type":"boolean"},"practitionerId":{"type":"string","nullable":true,"format":"uuid"}},"required":["onboarded","practitionerId"],"description":"Onboarding status response"}}}}}}},"/api/v1/compatibility/persons/by-user-ids":{"post":{"tags":["Compatibility"],"summary":"Get persons by user IDs","description":"Backward compatibility endpoint for legacy services.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonListRequest"}}}},"responses":{"200":{"description":"List of persons","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PersonResponse"}}}}}}}},"/api/v1/compatibility/practitioners/by-user/{userId}":{"get":{"tags":["Compatibility"],"summary":"Get practitioner by user ID (compatibility)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"Practitioner details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PractitionerResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/compatibility/organizations/{id}":{"get":{"tags":["Compatibility"],"summary":"Get organization by ID (compatibility)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Organization details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/compatibility/user/role":{"get":{"tags":["Compatibility"],"summary":"Get user role (compatibility)","responses":{"200":{"description":"User role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRoleResponse"}}}}}}},"/api/v1/compatibility/user/organizations":{"get":{"tags":["Compatibility"],"summary":"Get user organizations (compatibility)","responses":{"200":{"description":"User organizations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserOrganizationResponse"}}}}}}}},"/api/v1/organizations/{id}/billing/status":{"get":{"tags":["Billing"],"summary":"Get billing status","description":"Get subscription status, trial info, seat counts, and grace period status for an organization","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Billing status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingStatus"}}}}}}},"/api/v1/organizations/{id}/billing/checkout":{"post":{"tags":["Billing"],"summary":"Create checkout session","description":"Create a Stripe checkout session for purchasing seats. Requires canManageBilling capability. Frontend provides redirect URLs.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"priceId":{"type":"string","description":"Stripe price ID. If not provided, uses first HealthTalk product"},"seatQuantity":{"type":"integer","minimum":1},"successUrl":{"type":"string","format":"uri","description":"Frontend URL to redirect to after successful payment"},"cancelUrl":{"type":"string","format":"uri","description":"Frontend URL to redirect to if user cancels"}},"required":["seatQuantity","successUrl","cancelUrl"]}}}},"responses":{"200":{"description":"Checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"403":{"description":"Forbidden - requires canManageBilling","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/v1/organizations/{id}/billing/portal":{"post":{"tags":["Billing"],"summary":"Create customer portal session","description":"Create a Stripe customer portal session for managing subscription, payment method, and invoices. Requires canManageBilling capability. Frontend provides redirect URL.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","format":"uri","description":"Frontend URL to redirect to when user is done with the portal"}},"required":["returnUrl"]}}}},"responses":{"200":{"description":"Portal URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"403":{"description":"Forbidden - requires canManageBilling","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/v1/organizations/{id}/billing/seats/quantity":{"patch":{"tags":["Billing"],"summary":"Update seat quantity","description":"Change subscription seat count. Increases are prorated (charged immediately for remaining days). Decreases take effect at next renewal. Requires canManageBilling capability.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"description":"New seat quantity"}},"required":["quantity"]}}}},"responses":{"200":{"description":"Seat quantity updated","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"quantity":{"type":"number"}},"required":["message","quantity"]}}}},"400":{"description":"Invalid request or cannot reduce seats","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden - requires canManageBilling","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/v1/organizations/{id}/billing/seats":{"get":{"tags":["Billing"],"summary":"List seat assignments","description":"List which team members have seats assigned. Requires canManageBilling capability.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Seat assignments","content":{"application/json":{"schema":{"type":"object","properties":{"seats":{"type":"array","items":{"type":"object","properties":{"practitionerId":{"type":"string","format":"uuid"},"displayName":{"type":"string"},"hasSeat":{"type":"boolean"},"canManageMembers":{"type":"boolean"},"canManageTemplates":{"type":"boolean"},"canManageSettings":{"type":"boolean"},"canManageBilling":{"type":"boolean"}},"required":["practitionerId","displayName","hasSeat","canManageMembers","canManageTemplates","canManageSettings","canManageBilling"]}}},"required":["seats"]}}}},"403":{"description":"Forbidden - requires canManageBilling","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/v1/organizations/{id}/billing/seats/{practitionerId}":{"put":{"tags":["Billing"],"summary":"Assign or unassign seat","description":"Assign or unassign a seat to a team member. Requires canManageBilling capability and available seats.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"practitionerId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"hasSeat":{"type":"boolean"}},"required":["hasSeat"]}}}},"responses":{"200":{"description":"Seat assignment updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"No available seats","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden - requires canManageBilling","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/v1/organizations/{id}/notifications":{"get":{"tags":["Billing","Notifications"],"summary":"Get organization notifications","description":"Get all notifications for an organization (trial ending, payment failed, etc)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Notifications","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"message":{"type":"string"},"type":{"type":"string","enum":["trial_ending","payment_failed","grace_period_ending","complimentary_granted"]},"read":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","title","message","type","read","createdAt"]}}},"required":["notifications"]}}}}}}},"/api/v1/organizations/{id}/notifications/{notifId}/read":{"post":{"tags":["Notifications"],"summary":"Mark notification as read","description":"Mark a notification as read","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"notifId","in":"path"}],"responses":{"200":{"description":"Notification marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}}}}},"/api/v1/billing/products":{"get":{"tags":["Billing"],"summary":"Get available products","description":"Get list of HealthTalk products available for purchase. Returns products with metadata HealthtalkOnline=Yes.","responses":{"200":{"description":"Available products","content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"prices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"currency":{"type":"string"},"unitAmount":{"type":"integer","nullable":true},"interval":{"type":"string","nullable":true,"enum":["month","year"]},"taxBehavior":{"type":"string","nullable":true,"enum":["exclusive","inclusive","unspecified"]}},"required":["id","currency","unitAmount","interval","taxBehavior"]}}},"required":["id","name","description","metadata","prices"]}}},"required":["products"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/practitioners/me/pending-licenses":{"get":{"tags":["Billing","Practitioners"],"summary":"Get pending licenses","description":"Get pending licenses for current user. These are licenses from Stripe invoices not yet assigned to an organization.","responses":{"200":{"description":"Pending licenses","content":{"application/json":{"schema":{"type":"object","properties":{"pendingLicenses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"license_count":{"type":"integer"},"product_ids":{"type":"array","items":{"type":"string"}},"customer_email":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["id","license_count","product_ids","customer_email","created_at"]}},"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["pendingLicenses","organizations"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Billing","Practitioners"],"summary":"Assign pending license to organization","description":"Assign a pending license to one of the user's organizations. The licenses will be added to the organization's seat count.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pendingLicenseId":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"}},"required":["pendingLicenseId","organizationId"]}}}},"responses":{"200":{"description":"License assigned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - not a member of the organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/admin/billing/complimentary":{"post":{"tags":["Billing","Admin"],"summary":"Grant complimentary seats","description":"Grant complimentary (free) seats to an organization. System admin only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"quantity":{"type":"integer","minimum":1},"reason":{"type":"string"}},"required":["organizationId","quantity"]}}}},"responses":{"201":{"description":"Complimentary seats granted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]}}}},"403":{"description":"Forbidden - system admin only","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/admin/billing/complimentary/{id}":{"delete":{"tags":["Billing","Admin"],"summary":"Revoke complimentary seats","description":"Revoke complimentary seats from an organization. Automatically unassigns seats (LIFO). System admin only.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1}},"required":["quantity"]}}}},"responses":{"204":{"description":"Complimentary seats revoked"},"403":{"description":"Forbidden - system admin only","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Complimentary seat record not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}}}