Skip to content

Cases

Filing an asynchronous consult, the statuses it moves through, and what falls out of a decision.

A case is one asynchronous consult: what was requested, what the patient answered, and where a licensed clinician has taken it. It is the centre of the API. Everything else either feeds a case (patients, consents, files) or falls out of one (messages, prescriptions, orders, events).

Filing one

POST /v1/cases needs cases:write and four things:

  • patient, a pat_… id.
  • requested_medications, one to ten lines, each naming a medication_id from GET /v1/medications with a quantity, refills, days supply and label directions of at most 140 characters. You may name a pharmacy_id, add pharmacy_notes, or set no_substitutions.
  • answers, one to three hundred intake answers. Each carries a stable id, the question as the patient saw it, the answer, and a type of boolean, number, string or choice. Set important or critical to raise an answer in the clinician's view.
  • consent_ids, the consents this case is filed under. A case filed without a current telehealth and privacy consent answers 409 consent_required.

external_id is your own key for the case, unique within the organization. metadata is up to sixteen string keys of your own state.

Set hold: true to park the case at received instead of queueing it, which is what you want when you still have files to attach. POST /v1/cases/{id}/release_hold queues it.

The statuses

received -> queued -> in_review -> approved
                                -> declined
         waiting_on_patient <-> in_review
         waiting_on_integration <-> in_review

received is where a held case waits. waiting_on_patient and waiting_on_integration mean the clinician asked a question and named who should answer it. cancelled is yours to cause; withdrawn is an approval later voided. Both approved and declined set decided_at, and a decline sets decline_reason, which is written for the patient to read.

GET /v1/cases/{id}/status_history returns every status the case has held, oldest first, each with the status it came from and when it changed. That is an audit you can serve yourself rather than reconstruct from events.

Cancelling

POST /v1/cases/{id}/cancel withdraws a case that has not been decided, with an optional reason. A case that has already been decided answers 409 conflict.

What an approval produces

An approved case produces prescriptions, and a prescription that is dispensable produces an order. Both are read-only over this API, because both are decisions made downstream of you.

  • GET /v1/cases/{id}/prescriptions needs cases:read. A line carries dispensable. When it is false the clinician recorded the line for the record and no pharmacy will fill it, so do not bill or ship against it. days_supply is null on a live prescription, because a clinician's plan item has no such field.
  • GET /v1/cases/{id}/orders and GET /v1/orders/{id} need orders:read. An order moves through preparing, at_pharmacy, shipped, delivered, and carries tracking once the pharmacy sets it. blocked always names a reason of address, prescriber, payment or attestation, each saying what has to be fixed. failed is terminal.

Talking on a case

GET /v1/cases/{id}/messages reads the thread and POST writes to it. Reading needs messages:read, writing needs messages:write. You post as patient or as integration; the clinician's replies arrive on the same thread and are never writable by you.

Following one

Every transition emits an event. Poll GET /v1/cases/{id}/events for one case, poll GET /v1/events for all of them, or subscribe a webhook endpoint. Every event carries the resource's status after the transition, so a delivery that arrives late is still safe to apply. See Webhooks and Events and realtime.

To drive all of this without a clinician, see Test mode.