{"openapi":"3.1.0","info":{"title":"Praxis API","version":"1.0.0","description":"Praxis lets a connected EMR hand patients off, receive clinical updates as signed HL7 FHIR R4 webhooks, and pull the same data on demand for backfill and recovery. This document covers /api/v1 (patient hand-off), /api/fhir/r4 (read-only FHIR), and the partner provisioning endpoint. Internal admin and platform-admin routes are not part of this public contract and are never included here."},"servers":[{"url":"https://praxisnp.co","description":"Production"}],"tags":[{"name":"Patients","description":"Patient hand-off and state (/api/v1)."},{"name":"Messages","description":"Provider-to-patient text messaging: patient replies, delivery status, and opt-out (/api/v1). Requires messaging to be enabled for the connection."},{"name":"Patient Edits","description":"Accept or reject a clinical support user's change to a patient field, sent to you as a `patient.update` webhook (/api/v1). Only sent when patient edits are enabled for the connection."},{"name":"Partner","description":"Platform-level partner tenant provisioning (/api/v1/partner)."},{"name":"Discovery","description":"Unauthenticated FHIR self-description."},{"name":"Patient","description":"FHIR Patient resource."},{"name":"Encounter","description":"FHIR Encounter resource (visits)."},{"name":"DocumentReference","description":"FHIR DocumentReference resource (signed SOAP notes)."},{"name":"MedicationRequest","description":"FHIR MedicationRequest resource (prescriptions)."},{"name":"ServiceRequest","description":"FHIR ServiceRequest resource (lab orders)."},{"name":"Practitioner","description":"FHIR Practitioner resource."}],"components":{"securitySchemes":{"apiKeyBearer":{"type":"http","scheme":"bearer","description":"A Praxis API key (`prx_live_...`), sent as `Authorization: Bearer <key>`."},"provisionBootstrapKey":{"type":"apiKey","in":"header","name":"x-provision-bootstrap-key","description":"Platform-level partner-provisioning key. Not part of the per-connection credential model."}},"schemas":{"EmrError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_api_key","connection_paused","not_found","conflict","payload_too_large","validation_failed","idempotency_key_reuse","rate_limited","internal_error","messaging_disabled","patient_opted_out","message_too_long","thread_not_found"],"description":"Stable machine-readable error code -- branch on this, not on message. Reserved (documented, but no /api/v1 route returns them yet): patient_opted_out, thread_not_found."},"message":{"type":"string","description":"Human-readable explanation."},"details":{"type":"array","description":"Present on 422 validation_failed: one entry per invalid field.","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}},"required":["field","message"]}}},"required":["code","message"]}},"required":["error"]},"FhirOperationOutcome":{"type":"object","properties":{"resourceType":{"type":"string","enum":["OperationOutcome"]},"issue":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string","enum":["fatal","error"]},"code":{"type":"string","enum":["security","forbidden","not-found","invalid","throttled","exception"],"description":"Stable machine-readable issue code."},"diagnostics":{"type":"string","description":"Human-readable explanation."}},"required":["severity","code","diagnostics"]}}},"required":["resourceType","issue"]},"FhirResource":{"type":"object","description":"A HL7 FHIR R4 resource (Patient, Encounter, DocumentReference, MedicationRequest, ServiceRequest, Practitioner, or Task). See the FHIR mapping guide for field-level detail and status-code mappings.","properties":{"resourceType":{"type":"string"}},"required":["resourceType"],"additionalProperties":true},"FhirSearchsetBundle":{"type":"object","properties":{"resourceType":{"type":"string","enum":["Bundle"]},"type":{"type":"string","enum":["searchset"]},"total":{"type":"integer","description":"Number of entries in this page."},"entry":{"type":"array","items":{"type":"object","properties":{"resource":{"$ref":"#/components/schemas/FhirResource"}}}},"link":{"type":"array","description":"Present only when another page exists.","items":{"type":"object","properties":{"relation":{"type":"string","enum":["next"]},"url":{"type":"string"}}}}},"required":["resourceType","type","total","entry"]},"FhirCollectionBundle":{"type":"object","description":"entry[0] is always the primary resource for this event; related Patient/Practitioner/Encounter resources follow so the receiver never needs a follow-up call.","properties":{"resourceType":{"type":"string","enum":["Bundle"]},"type":{"type":"string","enum":["collection"]},"entry":{"type":"array","items":{"type":"object","properties":{"resource":{"$ref":"#/components/schemas/FhirResource"}}}}},"required":["resourceType","type","entry"]}}},"webhooks":{"visit.status_changed":{"post":{"summary":"visit.status_changed delivery","description":"A visit moves between queued/assigned/in_progress/completed/cancelled. Primary resource: Encounter. Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"note.signed":{"post":{"summary":"note.signed delivery","description":"A visit is completed via Confirm & Sign. A draft save never fires this event. Primary resource: DocumentReference. Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"script.created":{"post":{"summary":"script.created delivery","description":"A prescription is written. Primary resource: MedicationRequest. Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"script.status_changed":{"post":{"summary":"script.status_changed delivery","description":"A script's status changes. Primary resource: MedicationRequest. Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"script.shipment_updated":{"post":{"summary":"script.shipment_updated delivery","description":"Shipping or tracking information updates. Primary resource: MedicationRequest (plus a Task for shipment detail). Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"lab_order.created":{"post":{"summary":"lab_order.created delivery","description":"A lab order is created. Primary resource: ServiceRequest. Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"lab_order.status_changed":{"post":{"summary":"lab_order.status_changed delivery","description":"A lab order's status changes. Primary resource: ServiceRequest. Payload is a FHIR Bundle (type: collection); entry[0] is the primary resource, followed by related Patient/Practitioner/Encounter resources so no follow-up call is needed.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FhirCollectionBundle"}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff (1m, 5m, 30m, 2h, 6h, 24h), then the delivery is marked failed. 3 consecutive fully-failed deliveries (not attempts of one) auto-pause the connection, halting further deliveries until an administrator resumes it."}}}},"refill_checkin.decided":{"post":{"summary":"refill_checkin.decided delivery","description":"A provider approves, holds or discontinues a refill check-in. Payload is a small JSON object, not a FHIR Bundle. Dedupe redeliveries on X-Praxis-Event-Id.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"checkinRef":{"type":"string","description":"The checkinRef sent when the refill check-in was created."},"decision":{"type":"string","enum":["approve","hold","discontinue"]},"reason":{"type":"string","description":"The provider's documented reason; may be empty for approve."},"decidedAt":{"type":"string","format":"date-time"}},"required":["checkinRef","decision","reason","decidedAt"]}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with backoff."}}}},"message.outbound":{"post":{"summary":"message.outbound delivery","description":"A provider sent a text message to a patient. Send it to the patient over your own SMS channel, then report progress with POST /api/v1/messages/{messageId}/status. Only delivered when messaging is enabled for the connection. Payload is a small JSON object, not a FHIR Bundle. eventId is stable across Praxis's automatic redeliveries — dedupe those on eventId. A new eventId carrying an already-seen messageId is a provider-requested resend of a message that previously failed: send it again.","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eventId":{"type":"string","format":"uuid"},"messageId":{"type":"string","format":"uuid","description":"Use this id when reporting status."},"externalPatientId":{"type":"string","description":"The id this connection used when handing the patient off."},"body":{"type":"string","description":"The message text to send."},"sentAt":{"type":"string","format":"date-time"},"provider":{"type":"object","description":"The person who actually sent the message in Praxis.","properties":{"id":{"type":"string"},"displayName":{"type":"string"}},"required":["id","displayName"]},"onBehalfOf":{"type":"object","description":"Present only when clinical support sent the message on a provider's behalf: the np or partner it is sent for. provider stays the real sender. Praxis does not choose the name the patient sees; that is whatever your messaging channel shows.","properties":{"id":{"type":"string"},"displayName":{"type":"string"}},"required":["id","displayName"]}},"required":["eventId","messageId","externalPatientId","body","sentAt","provider"]}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful. A non-2xx or timeout schedules a retry with the same backoff as other events."}}}},"patient.update":{"post":{"summary":"patient.update delivery","description":"A clinical support user changed one EMR-owned field of a patient. Praxis has NOT changed its own copy: apply the change in your system, then answer with POST /api/v1/patient-edits/{editId} (`accept`, or `reject` with a reason). Praxis updates its copy only when you accept. Answer within 24 hours (`expiresAt`); after that the edit times out in Praxis and a late accept is not applied there. Only delivered when patient edits are enabled for the connection. Payload is a small JSON object, not a FHIR Bundle. Dedupe redeliveries on eventId (equal to editId). `value` shapes by field: `name` {firstName, lastName}; `phone` E.164 string; `email` string; `mailing_address` and `shipping_address` {line1, line2 (nullable), city, state (two-letter), zip}; `preferred_pharmacy` {name, phone (E.164, nullable), address (nullable)}; `allergies` and `current_medications` string arrays (empty means none); `height` inches and `weight` pounds as numbers; `date_of_birth` YYYY-MM-DD (confirmed twice by the support user).","parameters":[{"name":"X-Praxis-Event","in":"header","required":true,"description":"The event type, e.g. `visit.status_changed`.","schema":{"type":"string"}},{"name":"X-Praxis-Event-Id","in":"header","required":true,"description":"A UUID, stable across retries of the same delivery -- use it to dedupe.","schema":{"type":"string","format":"uuid"}},{"name":"X-Praxis-Api-Version","in":"header","required":true,"description":"Currently `v1`.","schema":{"type":"string"}},{"name":"X-Praxis-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature: `t=<unix-seconds>,v1=<hex hmac_sha256(secret, \"<t>.<raw-body>\")>`. Reject deliveries whose `t` is more than 5 minutes old (replay protection). See the \"Verifying signatures\" guide for Node and Python examples.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eventId":{"type":"string","format":"uuid","description":"Equal to editId; stable across redeliveries."},"editId":{"type":"string","format":"uuid","description":"Use this id when answering."},"externalPatientId":{"type":"string","description":"The id this connection used when handing the patient off."},"field":{"type":"string","enum":["name","phone","email","mailing_address","shipping_address","preferred_pharmacy","allergies","current_medications","height","weight","date_of_birth"]},"value":{"description":"The new, already-validated value. Shape depends on field (see above)."},"oldValue":{"description":"Praxis's copy before the change, or null when Praxis keeps none for this field."},"requestedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time","description":"Answer before this time (24 hours after requestedAt)."},"requestedBy":{"type":"object","description":"The clinical support user who made the change.","properties":{"id":{"type":"string"},"displayName":{"type":"string"}},"required":["id","displayName"]}},"required":["eventId","editId","externalPatientId","field","value","oldValue","requestedAt","expiresAt","requestedBy"]}}}},"responses":{"200":{"description":"Any 2xx response marks the delivery successful; it does not accept the edit. A non-2xx or timeout schedules a retry with the same backoff as other events."}}}}},"paths":{"/api/fhir/r4/DocumentReference":{"get":{"summary":"Search DocumentReference","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["DocumentReference"],"operationId":"getApiFhirR4DocumentReference","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"patient","in":"query","required":false,"description":"Praxis patient id. Omit to search every patient this connection handed off.","schema":{"type":"string"}},{"name":"_lastUpdated","in":"query","required":false,"description":"Only the `gt` prefix is supported, e.g. `gt2026-01-01T00:00:00Z`.","schema":{"type":"string"}},{"name":"_count","in":"query","required":false,"description":"Page size, 1-200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `link` entry with `relation: \"next\"`.","schema":{"type":"string"}}],"responses":{"200":{"description":"A searchset Bundle of signed notes. A draft (unsigned) note never appears, even once its text is autosaved.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirSearchsetBundle"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"422":{"description":"Malformed search input (bad `_lastUpdated`, out-of-range `_count`, or invalid `cursor`), as an `OperationOutcome` with issue code `invalid`.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/DocumentReference/{id}":{"get":{"summary":"Get a single DocumentReference","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["DocumentReference"],"operationId":"getApiFhirR4DocumentReferenceId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"DocumentReference id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The DocumentReference.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"404":{"description":"No DocumentReference with that id for this connection, or the visit's note is still a draft (unsigned notes have no FHIR representation).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/Encounter":{"get":{"summary":"Search Encounter","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Encounter"],"operationId":"getApiFhirR4Encounter","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"patient","in":"query","required":false,"description":"Praxis patient id. Omit to search every patient this connection handed off.","schema":{"type":"string"}},{"name":"_lastUpdated","in":"query","required":false,"description":"Only the `gt` prefix is supported, e.g. `gt2026-01-01T00:00:00Z`.","schema":{"type":"string"}},{"name":"_count","in":"query","required":false,"description":"Page size, 1-200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `link` entry with `relation: \"next\"`.","schema":{"type":"string"}}],"responses":{"200":{"description":"A searchset Bundle of Encounters, newest-updated last within a page.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirSearchsetBundle"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"422":{"description":"Malformed search input (bad `_lastUpdated`, out-of-range `_count`, or invalid `cursor`), as an `OperationOutcome` with issue code `invalid`.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/Encounter/{id}":{"get":{"summary":"Get a single Encounter","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Encounter"],"operationId":"getApiFhirR4EncounterId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Encounter id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The Encounter.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"404":{"description":"No Encounter with that id for this connection.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/MedicationRequest":{"get":{"summary":"Search MedicationRequest","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["MedicationRequest"],"operationId":"getApiFhirR4MedicationRequest","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"patient","in":"query","required":false,"description":"Praxis patient id. Omit to search every patient this connection handed off.","schema":{"type":"string"}},{"name":"_lastUpdated","in":"query","required":false,"description":"Only the `gt` prefix is supported, e.g. `gt2026-01-01T00:00:00Z`.","schema":{"type":"string"}},{"name":"_count","in":"query","required":false,"description":"Page size, 1-200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `link` entry with `relation: \"next\"`.","schema":{"type":"string"}}],"responses":{"200":{"description":"A searchset Bundle of MedicationRequests. The raw Praxis script status is preserved in an extension (`urn:cofabri:praxis:script-status`) so no granularity is lost by the FHIR status mapping -- see the FHIR mapping guide.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirSearchsetBundle"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"422":{"description":"Malformed search input (bad `_lastUpdated`, out-of-range `_count`, or invalid `cursor`), as an `OperationOutcome` with issue code `invalid`.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/MedicationRequest/{id}":{"get":{"summary":"Get a single MedicationRequest","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["MedicationRequest"],"operationId":"getApiFhirR4MedicationRequestId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"MedicationRequest id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The MedicationRequest.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"404":{"description":"No MedicationRequest with that id for this connection.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/metadata":{"get":{"summary":"CapabilityStatement","description":"The one unauthenticated route under /api/fhir/r4 -- an EMR discovers how to authenticate before it has anything to authenticate with.\n\nNo authentication required.","tags":["Discovery"],"operationId":"getApiFhirR4Metadata","security":[],"responses":{"200":{"description":"A FHIR CapabilityStatement.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}}}}},"/api/fhir/r4/Patient":{"get":{"summary":"Search Patient by identifier","description":"The only supported Patient search. Never paginates (there is at most one match).\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patient"],"operationId":"getApiFhirR4Patient","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"identifier","in":"query","required":true,"description":"In the form `system|value`, e.g. `urn:cofabri:praxis:external-patient-id|<externalPatientId>`.","schema":{"type":"string"}}],"responses":{"200":{"description":"A searchset Bundle with zero or one entry.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirSearchsetBundle"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"422":{"description":"The `identifier` parameter is missing or not in `system|value` form (issue code `invalid`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/Patient/{id}":{"get":{"summary":"Get a single Patient","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patient"],"operationId":"getApiFhirR4PatientId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Patient id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The Patient.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"404":{"description":"No Patient with that id for this connection.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/Practitioner/{id}":{"get":{"summary":"Get a single Practitioner","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Practitioner"],"operationId":"getApiFhirR4PractitionerId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Practitioner id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The Practitioner.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"404":{"description":"No Practitioner with that id visible to this connection.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/ServiceRequest":{"get":{"summary":"Search ServiceRequest","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["ServiceRequest"],"operationId":"getApiFhirR4ServiceRequest","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"patient","in":"query","required":false,"description":"Praxis patient id. Omit to search every patient this connection handed off.","schema":{"type":"string"}},{"name":"_lastUpdated","in":"query","required":false,"description":"Only the `gt` prefix is supported, e.g. `gt2026-01-01T00:00:00Z`.","schema":{"type":"string"}},{"name":"_count","in":"query","required":false,"description":"Page size, 1-200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `link` entry with `relation: \"next\"`.","schema":{"type":"string"}}],"responses":{"200":{"description":"A searchset Bundle of ServiceRequests (lab orders).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirSearchsetBundle"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"422":{"description":"Malformed search input (bad `_lastUpdated`, out-of-range `_count`, or invalid `cursor`), as an `OperationOutcome` with issue code `invalid`.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/fhir/r4/ServiceRequest/{id}":{"get":{"summary":"Get a single ServiceRequest","description":"Requires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["ServiceRequest"],"operationId":"getApiFhirR4ServiceRequestId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"ServiceRequest id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The ServiceRequest.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirResource"}}}},"401":{"description":"Missing, unknown, or revoked API key (issue code `security`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"403":{"description":"Connection paused, either by a Praxis administrator or automatically after 3 consecutive fully-failed webhook deliveries (issue code `forbidden`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"404":{"description":"No ServiceRequest with that id for this connection.","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"429":{"description":"Rate limit exceeded, includes a `Retry-After` header (issue code `throttled`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}},"500":{"description":"Unexpected server error (issue code `exception`).","content":{"application/fhir+json":{"schema":{"$ref":"#/components/schemas/FhirOperationOutcome"}}}}}}},"/api/v1/messages/{messageId}/status":{"post":{"summary":"Report delivery status of an outbound message","description":"Reports how an outbound message (one Praxis sent you in a `message.outbound` webhook) fared. Status advances `queued` → `sent` → `delivered`, and `failed` can be reported any time before `delivered`. `delivered` is final. A `failed` message can still become `sent` or `delivered` (your later report wins), so a failure report is not final. A late or out-of-order update (for example `sent` after `delivered`) is accepted with 200 and leaves the current status unchanged. Requires messaging to be enabled for the connection.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Messages"],"operationId":"postApiV1MessagesMessageIdStatus","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"messageId","in":"path","required":true,"description":"The `messageId` from the `message.outbound` webhook payload.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["sent","delivered","failed"]},"reason":{"type":"string","maxLength":200,"description":"Why delivery failed. Only used with `failed`."}},"required":["status"]}}}},"responses":{"200":{"description":"The message's status after this update.","content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","sent","delivered","failed"]}},"required":["messageId","status"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}},"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.\n\nAlso returned when: `messaging_disabled` -- messaging is not enabled for this connection. (Also `connection_paused`, see above.)"},"404":{"description":"`not_found` -- no outbound message with that id for this connection (an inbound message, another connection's message, or a malformed id).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- request body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` -- `status` must be `sent`, `delivered`, or `failed`; `reason` at most 200 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/partner/connections":{"post":{"summary":"Provision a partner tenant connection","description":"Creates a practice and a custom_emr connection through the same code path as the admin's \"Connect an EMR\" UI. Platform-level: gated by a single bootstrap key, not a per-connection API key. Idempotent on tenantIdentifier -- a repeat call for an already-provisioned tenant returns 409 conflict rather than reissuing credentials; reissuing goes through the normal per-connection key-rotation flow.\n\nRequires the `x-provision-bootstrap-key` header (a separate, high-privilege platform provisioning key -- not a per-connection API key).","tags":["Partner"],"operationId":"postApiV1PartnerConnections","security":[{"provisionBootstrapKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tenantIdentifier":{"type":"string","description":"The partner's own identifier for this tenant, at most 200 characters. Unique across all custom_emr connections; a repeat value returns 409 conflict."},"name":{"type":"string","description":"Practice / connection display name."},"webhookUrl":{"type":"string","description":"Must be HTTPS and resolve to a public address (checked at save time and at every delivery); rejected otherwise."}},"required":["tenantIdentifier","name","webhookUrl"]}}}},"responses":{"201":{"description":"Connection provisioned. Credentials are returned once, here, and never again.","content":{"application/json":{"schema":{"type":"object","properties":{"practiceId":{"type":"string","format":"uuid"},"connectionId":{"type":"string","format":"uuid"},"apiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"plaintext":{"type":"string","description":"Shown only in this response."},"prefix":{"type":"string"}},"required":["id","plaintext","prefix"]},"webhookSecret":{"type":"string","description":"Shown only in this response."}},"required":["practiceId","connectionId","apiKey","webhookSecret"]}}}},"401":{"description":"`invalid_api_key` -- missing or invalid `x-provision-bootstrap-key` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"409":{"description":"`conflict` -- a connection for this `tenantIdentifier` is already provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- body over 8,192 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` -- invalid JSON, a missing or too-long field (`tenantIdentifier`, `name`, `webhookUrl`), or a `webhookUrl` that is not HTTPS or resolves to a non-public address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- an unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patient-edits/{editId}":{"post":{"summary":"Accept or reject a patient edit","description":"Answers a `patient.update` webhook: Praxis sent you a change a clinical support user made to one patient field. Apply it in your system and send `accept`, or send `reject` with a short reason. Praxis updates its own copy of the patient only when you accept. Answer within 24 hours (the webhook's `expiresAt`): after that the edit is timed out in Praxis and a late `accept` is not applied there. The response `status` is the edit's state in Praxis after your call. A repeat answer is a 200 no-op that returns the existing state (`applied: false`), so if `status` differs from your decision, Praxis did not take it.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patient Edits"],"operationId":"postApiV1PatientEditsEditId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"editId","in":"path","required":true,"description":"The `editId` from the `patient.update` webhook payload.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["accept","reject"]},"reason":{"type":"string","maxLength":200,"description":"Why the change was rejected. Shown to the support user. Only used with `reject`."}},"required":["decision"]}}}},"responses":{"200":{"description":"The edit's state after this call.","content":{"application/json":{"schema":{"type":"object","properties":{"editId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["accepted","rejected","timed_out"]},"applied":{"type":"boolean","description":"True only for the call that accepted the edit and updated Praxis's copy of the patient."}},"required":["editId","status","applied"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"404":{"description":"`not_found` -- no patient edit with that id for this connection (another connection's edit, or a malformed id).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- request body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` -- `decision` must be `accept` or `reject`; `reason` at most 200 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patients":{"post":{"summary":"Hand off a patient","description":"Hands a patient off from a connected EMR to Praxis and creates their visit. Upserts on `externalPatientId` within this connection's practice -- re-sending never duplicates an active visit.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patients"],"operationId":"postApiV1Patients","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"1-255 printable ASCII characters, no spaces. A repeat call with the same key and body within 24 hours returns the original response with `Idempotent-Replayed: true`. Reusing a key with a *different* body returns 422 `idempotency_key_reuse`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"externalPatientId":{"type":"string","description":"This EMR's own id for the patient. Unique per practice per connection."},"firstName":{"type":"string"},"lastName":{"type":"string"},"state":{"type":"string","description":"Full US state name or two-letter abbreviation."},"treatmentCategory":{"type":"string","description":"Free text, stored verbatim. Known keys: weightLoss, longevity, sexualHealth -- any other non-empty string is also accepted."},"dateOfBirth":{"type":"string","format":"date"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":"string"},"city":{"type":"string"},"zip":{"type":"string"},"requiresSyncVisit":{"type":"boolean"}},"required":["externalPatientId","firstName","lastName","state","treatmentCategory"]}}}},"responses":{"200":{"description":"Existing patient updated. Returns their current active visit, or a newly created one if they had none active. Never creates a duplicate active visit.","content":{"application/json":{"schema":{"type":"object","properties":{"praxisPatientId":{"type":"string"},"visit":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}}}},"required":["praxisPatientId","visit"]}}}},"201":{"description":"New patient handed off and visit created.","content":{"application/json":{"schema":{"type":"object","properties":{"praxisPatientId":{"type":"string"},"visit":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}}}},"required":["praxisPatientId","visit"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"409":{"description":"`conflict` -- `externalPatientId` is already used in this practice by a different connection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- body over 65,536 characters (the real `MAX_BODY_CHARS` check counts UTF-16 code units, not bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` (field-level `details`) or `idempotency_key_reuse`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patients/{externalPatientId}":{"get":{"summary":"Get current patient state","description":"Current state of a patient this connection handed off: patient, active visit, and latest script status.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patients"],"operationId":"getApiV1PatientsExternalPatientId","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"externalPatientId","in":"path","required":true,"description":"The externalPatientId this connection used in POST /api/v1/patients.","schema":{"type":"string"}}],"responses":{"200":{"description":"The patient's current state.","content":{"application/json":{"schema":{"type":"object","properties":{"praxisPatientId":{"type":"string"},"externalPatientId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"state":{"type":["string","null"]},"treatmentCategory":{"type":["string","null"]},"visit":{"type":["object","null"],"properties":{"id":{"type":"string"},"status":{"type":"string"}},"description":"The current non-terminal visit (queued/assigned/in_progress), or null."},"latestScript":{"type":["object","null"],"properties":{"id":{"type":"string"},"status":{"type":"string"},"medicationName":{"type":"string"}}}}}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"404":{"description":"`not_found` -- unknown `externalPatientId`, or a patient handed off by a different connection (indistinguishable by design).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patients/{externalPatientId}/identity-verification":{"put":{"summary":"Set a patient's ID verification result","description":"Pushes the current ID verification snapshot for a patient so Praxis providers and admins can see it, read-only, on the patient chart. Praxis stores exactly one current snapshot per patient: the call is an idempotent upsert, and an update whose `updatedAt` is older (or equal) than the stored one is ignored (200) so out-of-order delivery is safe. Send only the fields below; ID images, ID numbers and anything else are rejected. A status of `none` stores the snapshot but shows no card.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patients"],"operationId":"putApiV1PatientsExternalPatientIdIdentityVerification","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"externalPatientId","in":"path","required":true,"description":"The externalPatientId this connection used in POST /api/v1/patients.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["none","awaiting_start","pending","needs_visual","failed","provider_error","passed","manually_verified","refunded"]},"level":{"type":["string","null"],"enum":["passive","visual_always",null]},"visualAttempts":{"type":"integer","minimum":0,"maximum":1000},"held":{"type":"boolean"},"failureReasons":{"type":"array","items":{"type":"string","maxLength":64},"description":"At most 20. Machine keys such as `dob_mismatch`, `name_mismatch`, `id_expired`."},"warnings":{"type":"boolean","description":"The provider flagged the verification for review."},"verifiedFirstName":{"type":["string","null"],"maxLength":200},"verifiedLastName":{"type":["string","null"],"maxLength":200},"verifiedDob":{"type":["string","null"],"description":"YYYY-MM-DD."},"idType":{"type":["string","null"],"maxLength":64,"description":"For example `drivers-license`."},"idState":{"type":["string","null"],"maxLength":64},"idExpires":{"type":["string","null"],"description":"YYYY-MM-DD."},"releasedAt":{"type":["string","null"],"description":"ISO 8601 timestamp the order was released, or null."},"manualReason":{"type":["string","null"],"maxLength":1000},"requestedByStaff":{"type":"boolean"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of this snapshot in the sender's system; used to ignore out-of-order updates."}},"required":["status","level","visualAttempts","held","failureReasons","warnings","verifiedFirstName","verifiedLastName","verifiedDob","idType","idState","idExpires","releasedAt","manualReason","requestedByStaff","updatedAt"]}}}},"responses":{"200":{"description":"Ignored: a snapshot with the same or a newer `updatedAt` is already stored. No body."},"204":{"description":"The snapshot was stored. No body."},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"404":{"description":"`not_found` -- no patient with that `externalPatientId` was handed off by this connection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- request body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` -- a field is missing, mistyped, over its limit, or unknown. `details` lists each field (values are never echoed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patients/{externalPatientId}/messages":{"post":{"summary":"Record a patient reply","description":"Records a text message the patient sent back, so the care team sees it in Praxis. Requires messaging to be enabled for the connection. A reply is recorded even if the patient has opted out of texts. Include `externalMessageId` (your own id for the message) to make delivery retries safe: a repeat with the same `externalMessageId` returns the original `messageId` with 200 and never creates a second message. The patient must already have been handed off by this connection; this call never creates a patient.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Messages"],"operationId":"postApiV1PatientsExternalPatientIdMessages","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"externalPatientId","in":"path","required":true,"description":"The externalPatientId this connection used in POST /api/v1/patients.","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"1-255 printable ASCII characters, no spaces. A repeat call with the same key and body within 24 hours returns the original response with `Idempotent-Replayed: true`. Reusing a key with a *different* body returns 422 `idempotency_key_reuse`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","maxLength":1600,"description":"The message text. Leading and trailing whitespace is trimmed; must not be blank. At most 1,600 characters (Unicode code points)."},"receivedAt":{"type":"string","format":"date-time","description":"When the patient sent it (ISO 8601). Defaults to now."},"externalMessageId":{"type":"string","maxLength":255,"description":"Your own id for this message (1-255 characters). Makes retries idempotent."}},"required":["body"]}}}},"responses":{"200":{"description":"A message with this `externalMessageId` was already recorded; returns the original ids. Nothing new is created.","content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string","format":"uuid"},"threadId":{"type":"string","format":"uuid"}},"required":["messageId","threadId"]}}}},"201":{"description":"Reply recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string","format":"uuid"},"threadId":{"type":"string","format":"uuid"}},"required":["messageId","threadId"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}},"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.\n\nAlso returned when: `messaging_disabled` -- messaging is not enabled for this connection. (Also `connection_paused`, see above.)"},"404":{"description":"`not_found` -- no patient with that `externalPatientId` was handed off by this connection (a patient handed off by a different connection is indistinguishable by design).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- request body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` (field-level `details`), `message_too_long` (body over 1,600 characters), or `idempotency_key_reuse`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}},"get":{"summary":"Read a patient message thread","description":"Returns the message thread for a patient this connection handed off, oldest first (the most recent 200 messages). A patient with no messages yet returns an empty list.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Messages"],"operationId":"getApiV1PatientsExternalPatientIdMessages","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"externalPatientId","in":"path","required":true,"description":"The externalPatientId this connection used in POST /api/v1/patients.","schema":{"type":"string"}}],"responses":{"200":{"description":"The thread.","content":{"application/json":{"schema":{"type":"object","properties":{"smsOptOut":{"type":"boolean","description":"True when the patient has opted out of texts; Praxis then refuses to send them new messages."},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"direction":{"type":"string","enum":["outbound","inbound"],"description":"`outbound` is from the care team to the patient; `inbound` is a patient reply."},"body":{"type":"string"},"status":{"type":"string","enum":["queued","sent","delivered","failed","received"]},"failureReason":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","direction","body","status","failureReason","createdAt"]}}},"required":["smsOptOut","messages"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}},"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.\n\nAlso returned when: `messaging_disabled` -- messaging is not enabled for this connection. (Also `connection_paused`, see above.)"},"404":{"description":"`not_found` -- no patient with that `externalPatientId` was handed off by this connection (a patient handed off by a different connection is indistinguishable by design).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patients/{externalPatientId}/messaging":{"put":{"summary":"Set a patient's text opt-out","description":"Tells Praxis whether the patient has opted out of texts (for example after replying STOP). While opted out, Praxis refuses to send the patient new messages. Creates the patient's message thread if it does not exist yet. Requires messaging to be enabled for the connection.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Messages"],"operationId":"putApiV1PatientsExternalPatientIdMessaging","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"externalPatientId","in":"path","required":true,"description":"The externalPatientId this connection used in POST /api/v1/patients.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"smsOptOut":{"type":"boolean","description":"true to opt the patient out, false to clear the opt-out."}},"required":["smsOptOut"]}}}},"responses":{"200":{"description":"The opt-out state now in effect.","content":{"application/json":{"schema":{"type":"object","properties":{"smsOptOut":{"type":"boolean"}},"required":["smsOptOut"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}},"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.\n\nAlso returned when: `messaging_disabled` -- messaging is not enabled for this connection. (Also `connection_paused`, see above.)"},"404":{"description":"`not_found` -- no patient with that `externalPatientId` was handed off by this connection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- request body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` -- `smsOptOut` must be a boolean.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/patients/{externalPatientId}/refill-checkins":{"post":{"summary":"Submit a refill check-in","description":"Creates a queued refill visit for a patient this connection already handed off, carrying the check-in flags for the provider. If the patient has an open refill visit with no check-in, the check-in attaches to it. If the patient has any other open visit (for example a new-patient visit) or the open visit already carries a different check-in, the call is rejected with 409. Idempotent on `checkinRef`: a repeat returns the same visit with 200. The patient must already have been handed off by this connection; this call never creates a patient.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patients"],"operationId":"postApiV1PatientsExternalPatientIdRefillCheckins","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"externalPatientId","in":"path","required":true,"description":"The externalPatientId this connection used in POST /api/v1/patients.","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"1-255 printable ASCII characters, no spaces. A repeat call with the same key and body within 24 hours returns the original response with `Idempotent-Replayed: true`. Reusing a key with a *different* body returns 422 `idempotency_key_reuse`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"checkinRef":{"type":"string","description":"Your unique id for this check-in (1-200 characters). Makes retries safe."},"program":{"type":"string","enum":["glp1","nad","sermorelin"]},"dueDate":{"type":"string","format":"date","description":"When the refill is due, YYYY-MM-DD."},"stateConfirmed":{"type":"string","description":"The state the patient confirmed in the check-in (up to 50 characters)."},"hardStops":{"type":"array","items":{"type":"string"},"description":"Answers that block a refill (at most 50 items, 300 characters each)."},"reviewFlags":{"type":"array","items":{"type":"string"},"description":"Answers the provider should review (at most 50 items, 300 characters each)."},"subscriptionPeriodDays":{"type":"integer","minimum":1,"maximum":400,"description":"Optional; null or omitted both mean unknown. The length in days of the patient's subscription billing period (1-400). Used to cap the days supply of the refill prescription."}},"required":["checkinRef","program","dueDate","stateConfirmed","hardStops","reviewFlags"]}}}},"responses":{"200":{"description":"A visit already exists for this `checkinRef`, or the check-in attached to the patient's open refill visit. No new visit created.","content":{"application/json":{"schema":{"type":"object","properties":{"visit":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}},"required":["visit"]}}}},"201":{"description":"Refill visit created.","content":{"application/json":{"schema":{"type":"object","properties":{"visit":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}},"required":["visit"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"404":{"description":"`not_found` -- no patient with that `externalPatientId` was handed off by this connection (a patient handed off by a different connection is indistinguishable by design).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"409":{"description":"`conflict` -- either `details[0].message` is `checkin_ref_in_use` (the `checkinRef` is already used for a different patient in this practice) or `open_visit_has_checkin` (the patient's open visit is not a refill visit or already carries a different check-in; nothing was changed -- retry after that visit completes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` (field-level `details`) or `idempotency_key_reuse`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}},"/api/v1/refill-checkins/{checkinRef}/payment-confirmed":{"post":{"summary":"Confirm payment for a refill check-in","description":"Tells Praxis the patient has paid for an approved refill. Only then is the provider's approved prescription released to the pharmacy. Idempotent: calling it again returns `already_released` and sends nothing. If the pharmacy network rejects the prescription for another reason (a 4xx answer) the call fails with 500 `internal_error` and the same call can safely be retried. If the prescription is permanently rejected by a pharmacy limit (the days supply is over the product limit, or the medication is unavailable or no longer matches), the call fails with 422 `validation_failed`, message `release_rejected`, and `details[0].message` carries the machine code (for example `days_supply_exceeds_cap`); do not retry: the prescription stays awaiting payment and a human must re-approve it. If the outcome is uncertain (the pharmacy network answered 5xx, or the connection failed after the prescription was sent), the call fails with 500 `internal_error` and message `release_needs_review`: nothing is sent again automatically, this does not resolve by itself, and a Praxis administrator must check with the pharmacy network; keep retrying or alert.\n\nRequires `Authorization: Bearer <Praxis API key>` (`prx_live_...`). The key resolves to exactly one connection, and therefore one practice -- no request names a practice.","tags":["Patients"],"operationId":"postApiV1RefillCheckinsCheckinRefPaymentConfirmed","security":[{"apiKeyBearer":[]}],"parameters":[{"name":"checkinRef","in":"path","required":true,"description":"The `checkinRef` this connection used when submitting the refill check-in.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"paymentRef":{"type":"string","description":"Optional. Your own payment reference (up to 200 characters). Accepted for your records; not otherwise used."}}}}}},"responses":{"200":{"description":"`released` -- the prescription was just sent to the pharmacy. `already_released` -- it was sent earlier; nothing new was sent. `nothing_pending` -- no approved prescription is waiting for this check-in. `not_approved` -- the provider has not approved this refill (held, discontinued, or no decision yet).","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["released","already_released","nothing_pending","not_approved"]}},"required":["status"]}}}},"401":{"description":"`invalid_api_key` -- the API key is missing, unknown, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"403":{"description":"`connection_paused` -- either a Praxis administrator paused this connection, or it was paused automatically after 3 consecutive fully-failed webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"404":{"description":"`not_found` -- no check-in with that `checkinRef` was submitted by this connection (a ref from another connection is indistinguishable by design).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"413":{"description":"`payload_too_large` -- body over 65,536 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"422":{"description":"`validation_failed` -- the body is not a JSON object, or `paymentRef` is not a string of at most 200 characters. Also `release_rejected` (message) -- the pharmacy limit permanently rejected the approved prescription; `details: [{ field: \"script\", message: <code> }]`. Permanent: do not retry; a human must re-approve.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"429":{"description":"`rate_limited` -- 120 requests per 60 seconds per key. Includes a `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}},"500":{"description":"`internal_error` -- unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmrError"}}}}}}}}}