https://api.casexchange.com/api/public/v1
Authentication: pass your key in the X-API-Key header on every request. Responses use the standard envelope described in Response Format.
Field-by-field request and response schemas live in the API tab under Endpoints: the Med Cases, Med Appointments, Med Billing and MedEx Documents groups, plus Account (GET /me/doctors, GET /me/locations) and Webhooks. This page explains how those endpoints fit together and doesn’t repeat every field.
Provider-scoped API keys
A practice integrates with a provider-scoped key. Its prefix iscxpp_ (double p), followed by the tier:
CaseXchange issues provider keys. To request one, email help@casexchange.com. The plaintext key is shown once, when it is created, so store it in a secrets manager straight away.
A provider key acts for the whole practice. It sees every case that has been referred to the practice (or that the practice created), not just one clinician’s patients. Changes you make are recorded in the case history under
API key: <key name>, so give each integration a key with a recognisable name.
What a provider key can and cannot do
The CaseXchange law-firm endpoints (
/referrals, /received-referrals, /sent-cases, /firms and so on) are for firm keys.
Restricted-access practices
Some practices, typically records or fulfilment vendors rather than treating clinicians, are set up as restricted-access. A restricted-access practice’s key can still read cases, insurance and documents, and can upload documents to a case. It gets403 restricted_access on:
- the five lifecycle actions (acknowledge, accept, reject, start treatment, complete treatment)
- listing or reading referrals (
GET /med-cases/{id}/referralsandGET /med-cases/{id}/referrals/{referralId}) - every appointment and billing endpoint
- a document upload that names an
appointmentId(omitappointmentIdto upload to the case itself)
The referral lifecycle
A law firm creates a med case for its client and sends your practice a referral on that case. The referral, not the case, carries the status:{id} is the med case ID and {referralId} is the referral ID. Both are UUIDs, and the referral must belong to that case.
Every successful action returns 200 with the updated referral in data, including its new status and the timestamp for that step (acknowledgedAt, respondedAt, treatmentStartedAt or treatmentCompletedAt).
Accepting a sent referral
You don’t have to acknowledge before you accept. If the referral is still sent, accept runs two transitions: it acknowledges (sent → under_review) and then accepts (under_review → accepted). Each step is recorded separately in the case history and each sends its own webhook (referral.under_review, then referral.accepted).
If the acknowledge step succeeds and the accept step then fails, the referral is left under_review and the error is returned. under_review is a valid state to retry accept from.
Reject only works from under_review. To reject a referral that is still sent, acknowledge it first.
Rejection reasons
reject requires a reasonCode and accepts an optional reasonNote (up to 1,000 characters):
A missing or unknown
reasonCode returns 400 validation_error.
Pharmacies
If your practice is set up in CaseXchange as a pharmacy,start-treatment and complete-treatment return 400 with the message Treatment steps do not apply to pharmacy referrals. A pharmacy referral stays accepted while you fill it, and the firm closes it from there.
The notify flag
All five practice actions take an optional boolean notify in the JSON body. It defaults to true.
With notify: true, the action notifies people the same way the web app does:
- The firm gets an email about the status change, if the firm has chosen to receive status changes as they happen.
- On accept, the patient gets a text message saying the referral was accepted, if the patient has agreed to receive texts.
notify: false, CaseXchange skips both of those. Everything else is still recorded:
- the entry in the case history, which the firm sees in MedEx
- the
referral.*webhooks to your own subscriptions - notes written to the firm’s case-management system, for firms that have connected one (on accept, reject, start treatment and complete treatment, including your treatment summary)
- the line in the firm’s MedEx daily digest, for firms that receive one
notify only affects the five lifecycle actions. Appointment writes send their usual notifications whatever you pass. For example, the first appointment on a case triggers the firm’s first-appointment email, and naming a clinician who isn’t yet on the referral emails that clinician.
Finding your referral IDs
Every lifecycle action needs the case ID and the referral ID. To find them:- Page through
GET /med-cases(limitup to 100), optionally usingsearch, to get the cases referred to your practice. - For each case, call
GET /med-cases/{id}/referrals. A provider key sees only its own practice’s referrals on the case. Usually there’s one, but handle more than one. Each referral carries itsidand currentstatus. Archived referrals aren’t listed.
GET /med-cases/{id}/referrals/{referralId} returns a single referral, so you can check its current status at any time. Another practice’s referral returns 404.
You also get referral IDs from:
- Webhooks.
referral.sentarrives when a firm sends your practice a new referral, withreferralIdandmedCaseId. Every laterreferral.<status>event carries both IDs too. Webhooks have no backfill, so they only cover referrals sent after you subscribed. - Action responses. Each lifecycle action returns the referral, including its
idandmedCaseId. - Appointments. Each appointment returned by
GET /med-cases/{caseId}/appointmentscarries themedReferralIdit is linked to.
Integration walkthrough
1
Check the key and load your roster
GET /me is for firm keys. To check a provider key, call GET /me/doctors. It returns the practice’s active clinicians, and GET /me/locations returns its locations. You need these IDs to create appointments: doctorId comes from /me/doctors and providerLocationId from /me/locations.2
Subscribe to webhooks
Register an HTTPS endpoint with a See Webhooks for providers below for what you receive.
standard key. The response includes the signing secret, which is returned only this once.3
List the cases referred to you
GET /med-cases returns the practice’s cases, newest first. It accepts page, limit (up to 100, default 20), search (matches patient name, reference number or date of birth) and includeArchived=true.patientFirstName, patientLastName, patientDateOfBirth and incidentDate, before you accept, so you can match the referral to a patient record first. The firm decides which fields to fill in, so any of them can be null. Don’t make your match depend on a single field.4
Read one case and its insurance
GET /med-cases/{id} returns 404 for a case that isn’t referred to your practice. GET /med-cases/{id}/insurances returns the insurance on the case.5
Find your referral on the case
List the case’s referrals. A provider key only sees its own practice’s referrals. Use the The example is shortened; each referral has the same fields as the accept response below.
id of the referral you want to act on and check its status first.6
Acknowledge, then accept or reject
Acknowledge tells the firm you’ve seen the referral. Accept says you’ll take the patient. As described above, Response
accept also acknowledges a sent referral, so a single call is enough.200 OK (accept):7
Start treatment
accepted. Pharmacies can’t use this step.8
Record appointments
Appointments live on the case. To cancel an appointment, send
doctorId and providerLocationId are required on every create and update. Pass medReferralId to link the appointment to a specific referral. If you leave it out, the appointment is linked to your practice’s most active referral on the case."cancelled": true on update. You can’t combine it with a non-null attended in the same request.Premier-synced appointments. Some appointments come from a Premier practice-management sync ("source": "premier"). Premier owns their date, time and outcome:- Sending
appointmentDate,appointmentTime,attendedorcancelledon an update, even unchanged, returns400 premier_owned_field. - Deleting one returns
400 premier_managed. - You can still change the referral, clinician, location, category, injury and insurance on them.
- Creating an appointment that matches an existing Premier appointment (same day and clinician, within 30 minutes) returns
409 conflictwithexistingAppointmentIdindetails. Update that appointment instead.
9
Add billing lines
Billing lines belong to an appointment. Each new appointment comes with a A line’s
VISIT_FEE line that has no amounts yet, so update that line rather than adding a second visit fee. Add PROCEDURE or OTHER lines for anything else. Amounts are decimal strings, not numbers.kind can’t be changed after it is created. Use DELETE on the same path to remove a line.10
Upload documents
Send The MedEx Documents endpoint pages list the accepted categories.
multipart/form-data with the file in a field named file and a required category. You can add an appointmentId to attach the document to a visit, and a note.GET /med-cases/{caseId}/documents lists a case’s documents, and GET /med-cases/{caseId}/documents/{documentId} returns a download link.11
Complete treatment
summary is optional (up to 2,000 characters). It is stored on the referral and, for firms whose case-management system is connected, filed there as a note.in_treatment. After this, the firm closes the referral.Loading a backlog of existing patients
If you are bringing patients you already treat into CaseXchange, you’ll run the same calls for many referrals. There’s no bulk endpoint in the public API: loop over the per-referral endpoints.Rate limits
Limits apply over a sliding 60-second window:
An
accept that also acknowledges counts as one request. At 100 writes a minute, 1,900 accepts take about 20 minutes.
Discovery uses the read limit. Finding 1,900 referrals takes about 19 GET /med-cases pages at limit=100 plus one GET /med-cases/{id}/referrals per case, about 1,920 GETs in all. That’s roughly 7 minutes at the standard read limit of 300 a minute, or 4 minutes at the read_only limit of 500. Discovery can run on a separate read_only key, but all the practice’s keys share the 1,000 GET a minute cap. Read the X-RateLimit-Remaining and X-RateLimit-Reset headers and slow down before you reach the limit. A 429 carries a Retry-After header in seconds. Requests rejected with 429 still count towards the window, so wait for Retry-After before you send again rather than retrying at once.
A backlog loop
For each existing patient:- Find the case with
GET /med-cases(usesearchor page through withlimit=100) and match it to your patient record. Remember that patient fields can be blank. - Get your referral with
GET /med-cases/{id}/referralsand note itsidandstatus. Skip referrals that are already where you want them, and don’t try to acceptrejected,withdrawnorclosedones. POST .../acceptwith{ "notify": false }. This acknowledges and accepts in one call.- If the patient is already in treatment,
POST .../start-treatmentwith{ "notify": false }. - If treatment has finished,
POST .../complete-treatmentwith{ "notify": false }and, if you have one, asummary. - Optionally load past appointments and billing. Remember that
notifydoesn’t apply to appointment writes. For example, the first appointment on a case still triggers the firm’s first-appointment email.
Retries and idempotency
The lifecycle actions are not repeatable: each one only works from its source status. That makes retries safe to reason about:- A
409means the referral is no longer in a status that action can move it from. The message names the current status, for example:Cannot transition referral from "accepted" to "accepted". Allowed source statuses: under_review. If you retried after a timeout and the current status is the one you were moving to (or a later one), your first call succeeded. Record it and move on. - To confirm, read the referral with
GET /med-cases/{id}/referrals/{referralId}and check itsstatus. - If
accepton asentreferral fails after the acknowledge step, the referral isunder_review. Retryaccept. 400and404won’t succeed on retry without a change to the request.429and5xxcan be retried with back-off.
Errors
Errors use the standard envelope witherror.code and error.message. See Error Handling for the general rules.
Webhooks for providers
A subscription created with a provider key receives the MedEx events for your practice. The delivery mechanics (signature header, retries, the 5-active-subscription cap, theX-CaseXchange-Event, X-CaseXchange-Event-Id and X-CaseXchange-Delivery headers) are the same as for firm subscriptions. See Verify Signatures and Reliability.
Two things differ from the firm webhooks documented in the Webhooks tab:
- The body is the bare payload, with no envelope. Read the event name from the
X-CaseXchange-Eventheader. - The events are the MedEx catalogue below, not the CaseXchange referral events. A provider subscription receives all of them; there’s no per-event filter.
Referral events
fromStatuses lists the statuses the transition is allowed from, not necessarily the exact status the referral left. When CaseXchange support corrects a referral’s status, the event also carries "adminOverride": true.
referral.sent, fetch the case. The event carries only IDs. Call GET /med-cases/{medCaseId} for the patient and case details, and keep the referralId because you’ll need it for every action on that referral.
Other events
You also receive events for changes to the cases referred to you:case.created,case.updated,case.archivedinjury.*,insurance.*,appointment.*,prescription.*,diagnosis.*andbilling.*(created,updated,deleted)note.*(created,updated,deleted)document.uploaded,document.deleted
case.updated and case.archived for a firm’s case, which carry patient details, are sent only to practices whose referral on the case has been acknowledged. Before that, you get referral.sent and nothing else for the case.
Ignore echoes of your own changes
Your own API calls send webhooks to your own subscriptions too. Anaccept you make produces referral.accepted (and referral.under_review first, if it started from sent), and an appointment you create produces appointment.created. The payload doesn’t say who made the change. Keep track of the changes you’ve just made, for example by referralId and toStatus, and skip the matching events rather than processing them again.
Error Handling
Error envelope, status codes and troubleshooting.
Webhooks
Subscriptions, signatures, retries and delivery history.

