Skip to main content
Scopes are permissions that control which API endpoints your app can access. When a clinic installs your app, they grant the scopes your app has requested.

How Scopes Work

  1. When you create your app, you declare which scopes it needs
  2. When a clinic installs your app, they review and approve those scopes
  3. When you create an API key, you select which of the approved scopes it should have
  4. 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
If you call an endpoint without the required scope, you’ll receive a 403 Forbidden response.

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 the X-Session-Token header. It is verified for your app and the organization (401 authorizer_unverified otherwise), 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.
Each of that user’s personal ModMed logins in the organization is a mailbox: 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.
GET /v3/faxes/templates opens a live session against the practice’s EHR — unlike the other two reads, which are served from our own records. Each call draws from your app’s share of the practice’s EHR rate; over it the call fails with ehr_unavailable and can be retried with backoff. Cover templates are practice-wide and change rarely, so fetch them once and cache them rather than calling this before every send.

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

An added payer goes straight into the practice’s EHR payer list with default billing settings (non-contracted, accepts assignment, electronic claims when the payer takes them) and is usable for claims at once — tell the practice’s billing team so they can review it. There is no daily limit on added payers, so guard against loops in your app.

Notes

Tasks

Intramail (v4, ModMed)

Sent through the acting user’s personal ModMed login (the X-Session-Token user, else the API key’s creator). Idempotency-Key supported on sends.

Emails

Faxes

Faxes are sent through the practice’s own EHR fax subsystem, so anything you attach is filed as a chart document and becomes part of the medical record. A sent fax cannot be recalled — idempotencyKey is required, and repeating a call with the same key returns the original fax instead of sending another.There is no daily limit on sends: each send draws from your app’s share of the practice’s EHR rate, and over it POST /v3/faxes/send fails with ehr_unavailable; retry with the same idempotencyKey. Every send is a real fax, so guard against loops in your app.A repeat with the same idempotencyKey never dials — it returns the original record, so re-asking after an unknown outcome is safe. While the first send is still in flight you get a 503 rather than the record — repeat the same request shortly, and never mint a new key for it. A 409 means something else: the key was already used for a DIFFERENT fax, and only then should you use a new key. It is scoped to (app, organization) and never expires.

Prescriptions and E-Prescribing (v4, ModMed)

write:prescription_sends is its own consent group, separate from write:prescriptions: approving drafts never approves transmitting them. Sending is the prescriber’s signature in the EHR, it transmits over Surescripts, and it cannot be recalled through Max AI. Controlled substances are always refused.A draft copied from a prescription that carries a diagnosis is refused with 400 capability_unsupported for now, and so is voiding a draft.

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.
The response includes your granted scopes:

Insufficient Scope Error

If you call an endpoint without the required scope:
To resolve this, update your app’s requested scopes in the Max AI dashboard and have the clinic re-approve the updated permissions.