How Scopes Work
- When you create your app, you declare which scopes it needs
- When a clinic installs your app, they review and approve those scopes
- When you create an API key, you select which of the approved scopes it should have
- At runtime, your effective permissions are the intersection of the key’s scopes and the clinic’s installation grants, and each API endpoint checks for the required scope before returning data
Available Scopes
Read Scopes
Patient Data
Appointments
Providers
Facilities
Billing
Some opt-in parameters need a second scope, because
they return another resource’s data:
Without the extra scope the request is a 403, not a silently narrower response.
Claims
Payer Remittances (ERAs)
Per-procedure allowed, paid and patient-responsibility amounts, with the CARC/RARC
adjustments explaining the gap between charge and paid. Reported for both EHRs.
deductibleAmount, coinsuranceAmount and copayAmount are EzDerm only: the ModMed
ERA sync does not write those columns, so they are NULL on every ModMed line even when the
remit reported them. Do not derive patient responsibility by summing the three — on ModMed
that sum is 0 on a line the patient really owes. Use patientResponsibilityAmount, which
is populated for both EHRs.
Sync on updatedSince: payer amounts keep moving for 90+ days after service as
recoupments, secondary payers and 835 PLB adjustments land.
ERA Payments (v4)
A separate scope from
read:eras: it exposes the practice’s payment ledger (check totals and
trace numbers), not per-procedure remittance lines. Filter by checkNumber,
bankTransactionId or a check/deposit date range; the list is offset-paged.
Bank Deposits (v4)
Each deposit carries the bank’s description as printed (
description), which can name a patient. Account
and routing numbers and the Plaid connection are never shared. A
deposit’s status tells you whether it is still the row to use: follow supersededBy when a
pending deposit is replaced by its posted twin. Facility-limited API keys are refused on
these endpoints.
Notes
Tasks
Intramail (v4, ModMed)
Always one user’s own ModMed mail, read from Max’s copy, never anyone else’s:- The logged-in user when your app forwards their App Bridge token (
maxcare.appToken()) in theX-Session-Tokenheader. It is verified for your app and the organization (401 authorizer_unverifiedotherwise), as for e-prescribing sends. Send it on every request a user makes in an app several people use. - The API key’s creator when there is no
X-Session-Token, for your own background jobs on your own mail. Never use this path to serve another user.
GET /v4/intramail/status lists them and says whose mail it is (actingFor); pass a mailbox’s id (imb_…) as mailboxId to act on it, otherwise the one marked isDefault is used. Sync is off until that user turns it on in their Max settings (My Settings); it then runs every 5 minutes through their personal ModMed login and reads the newest 100 messages of each folder. While it is off, every intramail route except GET /v4/intramail/status answers 403 intramail_unavailable; Max keeps the stored copy, and turning sync back on serves it again. A facility-limited key sees staff-only mail and mail linked to a patient or note at its facilities; mail with a patient as author or recipient but no patient or note link is hidden from it, since its facility cannot be told. So a facility-limited key must link any message it sends with a patient recipient to a patient (patientId, or noteId; a reply keeps the original’s patient link, or its visit’s patient), or it gets 400.
Emails
Faxes
read:faxes also gates delivery of the fax.status_changed webhook.
Pharmacies and Prescriptions (v4, ModMed)
These reads open a live session against the practice’s EHR on every call, except
GET /v4/prescriptions, which serves the platform’s stored copy (see
E-Prescribing). Prescription and patient-pharmacy endpoints are ModMed only and answer 400 capability_unsupported on other EHRs. Directory search works on EZDerm too, but requires zip there
and cannot look up an NCPDP id.Patient Tracking (v4)
Snapshot expansions need more scopes, all of them:
include=patient needs read:patients;
contact needs read:patients and read:patient_pii; visitDetails needs read:appointments;
insurance needs read:patients and read:insurance_policies. A requested expansion without them
is a 403 include_not_permitted. See Patient Tracking.Phone Numbers
Inventory
Payer Directory
These endpoints are available on v1–v4. Existing installations and API keys must grant
read:payers before using the directory. Adding payer identity fields to already-authorized policies, claims, or ERA lines does not require this additional scope. See Payer identities.
Insurance Policies
Prior Authorizations
Eligibility Checks
Organization Members
Staff email addresses are personal data, so this is a scope of its own rather than part
of any other. Declare it in
optional_scopes so each organization opts in; an
organization that has not granted it gets 403 from the endpoint. (Declared as a
required scope instead, it is in effect for every installation of that version, like any
required scope.) Available on v1–v4.
Write Scopes
Patients
An exact insurance write needs no extra scope, but an app picks the
payerId from GET /payers, which
requires read:payers. A payerId outside the patient’s EHR instance, inactive, or without an EHR id is
refused with 422 payer_not_writable before anything is written.
Patient Documents
Payers
Notes
Tasks
Intramail (v4, ModMed)
Sent through the acting user’s personal ModMed login (theX-Session-Token user, else the API key’s creator). Idempotency-Key supported on sends.
Emails
Faxes
Prescriptions and E-Prescribing (v4, ModMed)
Products
Inventory
Eligibility Checks
Deposit Matches (v4)
A match is your app’s record only: it never changes Max AI’s ERA reconciliation. A deposit
already held by a confirmed ERA link, a card payout or another app is refused with
409 deposit_already_allocated, and allocations above the deposit amount with
409 over_allocated. Reading deposits needs read:bank_transactions as well.
Checking Your Scopes
Use the/v3/marketplace/me endpoint to see which scopes your API key currently has. This endpoint does not require any scope — it is always available.
