Create an eConsult
A case (eConsult) needs four things: a connected practice (you send its
practice key), a patient, an active provider from the practice’s
roster, and a clinical question for one or more specialties.
Attachments are optional but usually helpful.
Identify the patient
Section titled “Identify the patient”You have two options.
Create the patient with the case. Send the patient’s demographics in the
patient field of POST /cases, instead of patient_id. Thea finds or
creates the patient in the same request, so a patient only exists in Thea once
a consult is sent for them. See Create the case.
Or create the patient first with POST /patients, then reference them by
patient_id. This suits integrations that sync patients anyway.
curl -X POST "$THEA_API/patients" \ -H "Authorization: Bearer $THEA_PARTNER_KEY" \ -H "Thea-Practice-Key: $THEA_PRACTICE_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: patient-acme-pt-99812" \ -d '{ "external_id": "acme-pt-99812", "first_name": "Avery", "last_name": "Johnson", "date_of_birth": "1984-03-12" }'How matching works
Section titled “How matching works”Both ways, Thea looks only in the practice your practice key belongs to, for:
- A patient with the same
external_id, then - A patient with the same first name, last name, and date of birth.
Patients in other practices are never matched, even with the same
external_id, name, and date of birth.
If one matches, Thea uses that patient and updates the fields you sent (fields
you omit stay unchanged). Otherwise it creates a new patient. POST /patients
returns 200 for a match and 201 for a new patient; POST /cases always
returns 201 with the case. Always sending external_id makes matching exact.
Create the case
Section titled “Create the case”curl -X POST "$THEA_API/cases" \ -H "Authorization: Bearer $THEA_PARTNER_KEY" \ -H "Thea-Practice-Key: $THEA_PRACTICE_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: acme-ref-5521" \ -d '{ "patient": { "external_id": "acme-pt-99812", "first_name": "Avery", "last_name": "Johnson", "date_of_birth": "1984-03-12" }, "provider": { "npi": "1234567890" }, "created_by_email": "ma@riverbend.example", "specialty_ids": ["12"], "question": "55F with new-onset palpitations; Holter shows frequent PVCs (8% burden). Echo normal. Is a beta-blocker reasonable first-line, and when should she see cardiology in person?", "patient_consent_given": true, "external_ids": { "referral_id": "acme-ref-5521" } }'| Field | Notes |
|---|---|
patient or patient_id |
The patient’s demographics, or the id of a patient you created earlier. Send one, not both. |
provider |
The clinician the case is for, by roster id, npi, or email. They must be an active provider in the practice. Specialists address their answer to them, and they get Thea’s usual notifications. |
created_by_email |
Optional. An active staff member creating the case on the provider’s behalf. |
specialty_ids |
From GET /specialties. Usually one; send several if the question spans specialties. |
question |
The clinical question with relevant history, findings, and what you want answered. Specific questions get faster, more useful answers. |
patient_consent_given |
Must be true. You confirm the patient agreed to an eConsult. |
external_ids |
Optional. Your own IDs, such as referral_id. Thea echoes them on the case and on every webhook about it. |
The response is the new case with status: "open", your
external_ids, and a url that opens the case in Thea.
responding_specialists stays empty until a specialist replies.
If the provider has no active seat, for example because the admin removed it
after you showed the option, the request fails with
422 provider-not-active. Using your
referral ID as the Idempotency-Key makes retries safe.
Attach files
Section titled “Attach files”Attachments, such as your referral PDF, usually help the specialist. Upload
each file to the case in one request, as multipart/form-data. PDF, JPEG, PNG,
and HEIC files up to 25 MB are accepted. The file is posted in the case thread
from the requesting provider (or author_provider_id), and specialists see it
straight away.
curl -X POST "$THEA_API/cases/a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b/files" \ -H "Authorization: Bearer $THEA_PARTNER_KEY" \ -H "Thea-Practice-Key: $THEA_PRACTICE_KEY" \ -H "Idempotency-Key: acme-ref-5521-referral-pdf" \ -F "file=@referral-5521.pdf;type=application/pdf"import { readFile } from 'node:fs/promises'
const form = new FormData()form.append('file', new Blob([await readFile('referral-5521.pdf')], { type: 'application/pdf' }), 'referral-5521.pdf')
// Let fetch set the multipart Content-Type, so leave it out of the headers.const { 'Content-Type': _, ...authHeaders } = headersconst file = await fetch(`${THEA_API}/cases/${econsult.id}/files`, { method: 'POST', headers: { ...authHeaders, 'Idempotency-Key': 'acme-ref-5521-referral-pdf' }, body: form,}).then((r) => r.json())with open("referral-5521.pdf", "rb") as f: file = session.post( f"{THEA_API}/cases/{econsult['id']}/files", headers={"Idempotency-Key": "acme-ref-5521-referral-pdf"}, files={"file": ("referral-5521.pdf", f, "application/pdf")}, ).json(){ "id": "6d0e4f2a9b", "filename": "referral-5521.pdf", "content_type": "application/pdf", "size_bytes": 482113 }A file over 25 MB returns
413 file-too-large. To download any file
on a case, including ones specialists attach, call GET /files/{file_id} for a
link that works for 5 minutes.
Follow the case
Section titled “Follow the case”- Replies: each message triggers
case.message.created. See Case messaging. - Specialists: a specialist appears in
responding_specialistsonce they reply. Thea doesn’t share who a case is assigned to before then. - First response:
first_specialist_response_atrecords when a specialist first replied.
Close or reopen
Section titled “Close or reopen”When the question is answered, the provider closes the case, either in Thea or
through you on their behalf. Specialists can’t close cases. When you close one,
it’s recorded as closed by the requesting provider unless you name another
active provider:
curl -X POST "$THEA_API/cases/a1f3c9e27b4d4e0f9c6b2d8e5f7a1c3b/close" \ -H "Authorization: Bearer $THEA_PARTNER_KEY" \ -H "Thea-Practice-Key: $THEA_PRACTICE_KEY" \ -H "Content-Type: application/json" \ -d '{ "closed_by_provider_id": "2210" }'A closed case has status: "closed", a closed_at timestamp, and closed_by,
and its thread is read-only. POST /cases/{case_id}/reopen reopens it, with an
optional reopened_by_provider_id. Both calls are safe to repeat.
The consult report
Section titled “The consult report”Thea builds its consult report PDF in the web app when a provider closes the
case, so the API doesn’t return a PDF. To file a report in your system, wait for
case.closed, fetch GET /cases/{case_id} and GET /cases/{case_id}/messages,
and render the case and its conversation in your own format.
A case can be reopened and closed again (case.reopened, then case.closed),
so treat each close as a new version of the report.

