Skip to main content
This guide is for medical practices that connect an EHR, practice-management or billing system to Med Xchange (MedEx), the CaseXchange module through which law firms refer injured clients to medical providers. It covers what a practice’s API key can do, how a referral moves from “sent” to “treatment complete”, and how to load patients you are already treating. Base URL: 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 is cxpp_ (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 gets 403 restricted_access on:
  • the five lifecycle actions (acknowledge, accept, reject, start treatment, complete treatment)
  • listing or reading referrals (GET /med-cases/{id}/referrals and GET /med-cases/{id}/referrals/{referralId})
  • every appointment and billing endpoint
  • a document upload that names an appointmentId (omit appointmentId to upload to the case itself)
If you aren’t sure whether your practice is restricted, ask CaseXchange support.

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.
With 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: false means the firm hears about the change only through those records. It gets no email when it happens. That matters most for reject and complete-treatment, which the firm usually needs to act on. Use the default for live work and keep notify: false for loading history.
Use notify: false when you load patients you are already treating. Without it, every accept would email the firm and text a patient who is already coming to see you that their referral “was accepted”.
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:
  1. Page through GET /med-cases (limit up to 100), optionally using search, to get the cases referred to your practice.
  2. 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 its id and current status. 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.sent arrives when a firm sends your practice a new referral, with referralId and medCaseId. Every later referral.<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 id and medCaseId.
  • Appointments. Each appointment returned by GET /med-cases/{caseId}/appointments carries the medReferralId it 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 standard key. The response includes the signing secret, which is returned only this once.
See Webhooks for providers below for what you receive.
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.
You can read the patient’s details, including 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 id of the referral you want to act on and check its status first.
The example is shortened; each referral has the same fields as the accept response below.
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, accept also acknowledges a sent referral, so a single call is enough.
Response 200 OK (accept):
7

Start treatment

The referral must be accepted. Pharmacies can’t use this step.
8

Record appointments

Appointments live on the case. 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.
To cancel an appointment, send "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, attended or cancelled on an update, even unchanged, returns 400 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 conflict with existingAppointmentId in details. Update that appointment instead.
9

Add billing lines

Billing lines belong to an appointment. Each new appointment comes with a 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.
A line’s kind can’t be changed after it is created. Use DELETE on the same path to remove a line.
10

Upload documents

Send 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.
The MedEx Documents endpoint pages list the accepted categories. 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.
The referral must be 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:
  1. Find the case with GET /med-cases (use search or page through with limit=100) and match it to your patient record. Remember that patient fields can be blank.
  2. Get your referral with GET /med-cases/{id}/referrals and note its id and status. Skip referrals that are already where you want them, and don’t try to accept rejected, withdrawn or closed ones.
  3. POST .../accept with { "notify": false }. This acknowledges and accepts in one call.
  4. If the patient is already in treatment, POST .../start-treatment with { "notify": false }.
  5. If treatment has finished, POST .../complete-treatment with { "notify": false } and, if you have one, a summary.
  6. Optionally load past appointments and billing. Remember that notify doesn’t apply to appointment writes. For example, the first appointment on a case still triggers the firm’s first-appointment email.
Send one request at a time per key, or a small fixed number in parallel, and keep under the write limit.

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 409 means 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 its status.
  • If accept on a sent referral fails after the acknowledge step, the referral is under_review. Retry accept.
  • 400 and 404 won’t succeed on retry without a change to the request. 429 and 5xx can be retried with back-off.

Errors

Errors use the standard envelope with error.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, the X-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-Event header.
  • 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.
After 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.archived
  • injury.*, insurance.*, appointment.*, prescription.*, diagnosis.* and billing.* (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. An accept 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.