Skip to content

Scopes and organizations

What a credential may do, who it may do it for, and how a clinic grants you access.

Two things decide whether a call is allowed: the scopes on the credential, and the scopes the organization granted your integration. The API uses the intersection of the two, and never more.

The scope vocabulary

Scopes are resource:verb. There are twenty.

ScopeWhat it opens
organization:readGET /v1/organization, GET /v1/organizations
catalog:readMedications, pharmacies, states
patients:readReading patients, exports, case lists by patient
patients:writeCreating and updating patients, deletion requests
files:readReading file metadata
files:writeReserving, confirming and deleting files
consents:readReading a patient's consents
consents:writeRecording a consent
cases:readCases, their events, status history and prescriptions
cases:writeFiling, cancelling and releasing a case
messages:readThe message thread on a case
messages:writePosting to that thread
orders:readOrders raised from an approved case
visits:readReading visits, their slots and a join link
visits:writeBooking, cancelling and rescheduling a visit
webhooks:manageEndpoints, deliveries, redelivery
events:readThe event feed
realtime:subscribeMinting a realtime token
billing:readUsage and invoices
test:manageThe sandbox controls, test mode only

Every operation in the reference names the one scope it requires.

The intersection rule

A credential carries the scopes you ticked when you minted it. A grant carries the scopes the organization approved. The effective set is what both agree on.

A key with cases:write acting for an organization that granted only cases:read may read cases and nothing more. GET /v1/organization returns the effective set, so you can see what this credential can actually do here rather than infer it from a refusal.

A call outside the effective set answers 403 forbidden.

Naming the organization

An integration can hold approved grants to several organizations. The credential is the same; the organization is chosen per request.

  • One approved grant: it is used automatically and the header is optional.
  • More than one: send Cuvo-Organization: org_…. Without it the request answers 400 invalid_request, because guessing would be worse than refusing.
  • Naming an organization you hold no approved grant to answers 403 forbidden, which is the same answer as naming one that does not exist.
  • No approved grant at all answers 403 forbidden.
curl https://api.cuvo.co/v1/organizations \
  -H "Authorization: Bearer cuvo_sk_test_…"
{
  "data": [
    {
      "id": "org_c8k2q1w9",
      "name": "Northgate Health",
      "slug": "northgate",
      "kind": "DIRECT",
      "status": "active",
      "scopes": ["organization:read", "patients:write", "cases:write"]
    }
  ],
  "has_more": false
}

kind is how the organization reaches Cuvo: DIRECT, RESELLER, RESELLER_CLIENT, or INTEGRATION for a headless brand that exists only behind an API. It does not change what you can call. status is active or suspended.

Asking a clinic for access

A grant is requested in the portal, by an integration member with the ADMIN role or above. You need three things:

  1. The organization's public org_… id, which the clinic gives you. It is not guessable and cannot be looked up, so an id naming no organization is refused with a clear message rather than turned into a directory.
  2. The scopes you are asking for. Ask for what you need; a request for everything is a slower conversation.
  3. A note saying who you are and what you are building. The person approving it reads this.

The request appears as a pending grant. Once it is approved, the organization's name appears on your grant and the credential can act for it. Until then the clinic is a bare id on your side, so the form can never become an id-to-name lookup.

None of this blocks you from starting. Every new integration is created with an approved grant on the shared sandbox organization, so the whole loop is reachable before a clinic has heard of you.