Skip to main content
The prescription endpoints let an app renew a patient’s prescription and e-prescribe it to a pharmacy through the practice’s own EHR (ModMed EMA). They are v4 only and ModMed only; other EHRs answer 400 capability_unsupported.
Sending is the prescriber’s signature. In the EHR, releasing an eRx signs it, it transmits over Surescripts, and it cannot be recalled through Max AI. Controlled substances (C-II to C-V) are always refused, at draft and at send.

The flow

  1. Find the source. GET /v4/patients/{patientId}/prescriptions lists the patient’s prescriptions with their e-prescribing state (read:prescriptions).
  2. Pick the pharmacy. GET /v4/patients/{patientId}/pharmacies or GET /v4/pharmacies/search (read:pharmacies).
  3. Create a draft. POST /v4/prescriptions with sourcePrescriptionId and an explicit prescriberId (write:prescriptions). The draft is PENDING and reaches no pharmacy. It is read back and must match your request; if it does not, you get 502 ehr_write_mismatch with its id. The platform cannot void drafts (POST /v4/prescriptions/{id}/void answers 400 capability_unsupported), so staff discard it in EMA.
  4. Send it. POST /v4/prescriptions/{id}/send (write:prescription_sends, its own consent group).
  5. Follow it. GET /v4/prescriptions/{id} is always live; its erx.status moves initiated → sent → pharmacy_verified, or to error.

Stored prescriptions: the org-wide feed

GET /v4/prescriptions (read:prescriptions) lists the organization’s prescriptions across patients from the platform’s stored copy, without calling the EHR. Filter by dispensableId to find every patient with a given drug, or by patientId and status.
  • Freshness. The stored copy is refreshed in the background, within a daily per-practice budget. A prescription still moving (pending, or in e-prescribing) and written in the last 30 days is re-read about hourly; charts with an appointment in the last week or the next two weeks about daily; older charts once, during a one-year backfill. Every live read above also updates it. For one chart or one prescription right now, use the live endpoints.
  • Resolved at read. providerId and facilityId are looked up when the feed is read, so one can appear later (once that provider or location is synced) without updatedAt moving.
  • Change feed. updatedAt moves only when the stored prescription changes. Walk it with updatedSince set to the last row’s updatedAt and cursorId set to its id, always on page 1, until a short page; dedupe on id. Offset paging inside a window is refused.
  • Removed prescriptions carry deletedFromEhrAt once two consecutive complete EHR listings no longer return them, and appear only with includeDeleted=true.

Who sends

A send is released under the sending user’s own EHR login, never the practice’s system login. Forward the App Bridge token of the user who pressed Send as sessionToken, in the same request as the click. The platform verifies it, records that user as authorizedBy, and finds their personal EHR connection from it. If the user has no usable EHR login you get 409 no_sending_identity with data.reason: Call maxcare.pickProvider() so the user connects in place, then press Send again with the same idempotencyKey and a fresh token. Nothing was sent.

Idempotency: never send twice

idempotencyKey is required, scoped to your app and the organization, and never expires.
  • The same key for the same prescription and pharmacy returns the original send record and never sends again — even without a valid sessionToken, so you can learn a lost outcome after the token expired.
  • The same key for a different prescription or pharmacy is 409 idempotency_key_reused.
  • While the first request is still in flight you get 503: repeat the same request, never with a new key.
  • A prescription is never sent twice, whatever the key: while it has a send in flight, unknown or submitted, another send answers 409 prescription_send_exists with that send’s id. Only the app and key that created a send can read its outcome. An unknown send that never settles blocks its prescription for good; have staff check the prescription in EMA before creating a new draft, because a second draft to the same pharmacy is not blocked.
  • Every check that refuses before sending (not pending, controlled, validation, pharmacy capability, no_sending_identity, …) frees the key.

Send status

The platform never re-issues a send. An unknown send is settled by re-reading the prescription in the EHR about every 10 minutes. submitted from that re-read means the EHR shows the prescription released; it does not confirm the pharmacy, so check erx.pharmacy.name before telling the user where it went (erx.pharmacy.ncpdpId is usually null from EMA). A send that never reached the EHR reads not_sent only once the prescription has been PENDING for more than 60 minutes after the request, so about 60–70 minutes; a request lost in flight answers 503 for the first 15–25 minutes. While a send is unknown or answers 503, keep repeating the request with the same idempotencyKey; never send again under a new key. Some unknown sends never settle, and none is re-checked after 7 days: stop repeating then, and have staff check the prescription in EMA before creating a new draft.

Limits

There is no daily limit on sends. Each send draws from your app’s share of the practice’s EHR rate, and over it the send fails with ehr_unavailable; retry with backoff. Every send is a real prescription, so guard against loops in your app.