Skip to content

API reference / Authentication

Bearer tokens, in detail

Every authenticated HyperCRM API request carries one header: `Authorization: Bearer <token>`. This page covers exactly what that token can be, how a malformed or invalid header is rejected, and the specific error code returned for each failure mode.

The header

Every authenticated endpoint requires Authorization: Bearer <token>. The REST endpoints under /api/v1 accept a Firebase ID token from a signed-in HyperCRM session. The MCP endpoint (/api/v1/mcp) also accepts an opaque OAuth access token (hcrm_at_...) issued by HyperCRM's own OAuth 2.1 authorization server. There, a token starting with hcrm_at_ is checked as an OAuth token, and anything else is verified as a Firebase ID token. A REST call made with an hcrm_at_ token is rejected with 401.

No separate signup step

There is no API-key dashboard and no registration endpoint for a Firebase-authenticated caller. On the first successful verification of a given Firebase identity, HyperCRM finds or creates the corresponding user. A first-time caller with no practice gets a new practice with themselves as owner, and an invitation carries whichever role it was sent with, owner or staff. GET /me is the one exception: for a caller with a confirmed address and a still-pending invitation it returns no practice rather than inventing one, so somebody who signs in before clicking their link does not end up with an empty practice of their own competing with the one that invited them. A first call to any other endpoint does create the practice. Every subsequent call resolves the same practice from the token, and a request never supplies its own practiceId.

OAuth access tokens

For MCP clients, HyperCRM issues its own opaque hcrm_at_... access tokens and hcrm_rt_... refresh tokens through a standards-compliant OAuth 2.1 flow: dynamic client registration, a consent screen backed by the same Firebase sign-in, and a mandatory PKCE (S256) authorization code exchange. Tokens are never JWTs, only their SHA-256 hash is stored, and a refresh token rotates on every use, invalidating the previous one.

Access tokens expire after one hour. A refresh token unused for 30 days expires. No chain outlives 90 days from approval. A password reset, an email change, removal from the practice or a disconnect ends it sooner. Approving a client needs a sign-in within the last 10 minutes (reauth_required, 403), a confirmed email address, and a practice on Complete.

When authentication fails

A missing header returns missing_authorization; a header that isn't Bearer <token> returns invalid_authorization_header; an unparseable or unverifiable token returns invalid_auth_token or auth_token_verification_failed. An expired token returns expired_auth_token, and a revoked one returns revoked_auth_token. All six of these are HTTP 401. A disabled account is HTTP 403, not 401: the token itself still verifies correctly, but the identity behind it is disabled, surfaced as disabled_auth_token when Firebase flagged it, and as forbidden when the HyperCRM account's own status is not active. (user_disabled is an audit-log action name, not a response code.) unsupported_auth_provider (403) covers a sign-in provider HyperCRM doesn't accept for API access; auth_provider_email_required is defined for a provider that supplies no email address, and nothing throws it today. auth_configuration_missing (503) means the server itself has no Firebase project configured.

mfa_required (403) means the practice requires passkey verification, which a bearer token alone cannot satisfy. email_not_verified (403) means the action needs a confirmed email address, for example a CSV export. auth_email_conflict (409) means another sign-in identity already owns this email address. session_ended (401) means a staff sign-in session has ended.

See the full error reference for how these codes fit into the overall error envelope, and the API reference hub for the other resource pages.

Common questions

Patient records are encrypted, both where they are stored and on their way to your screen. Files are private, and a link to one stops working after a short time. Changes to patient records are recorded, with who made them and when. There is no shared practice password: everyone has their own account. On the Complete plan, the practice owner can also require two-step sign-in for the whole team.

Only on the MCP endpoint (/api/v1/mcp), using an OAuth access token instead. HyperCRM runs its own OAuth 2.1 authorization server for MCP clients: a client registers itself, a user approves access through a consent screen backed by their normal Firebase sign-in, and the client receives an opaque access token to use as the bearer token. The REST endpoints under /api/v1 take a Firebase ID token only.

Because the token itself is still valid and verifies correctly. It's the account behind it that's disabled, which is a distinct failure from an invalid or expired credential. HyperCRM treats that as an authorization problem (403, forbidden) rather than an authentication problem (401, unauthenticated), and documents it as a deliberate exception.

Yes. Access tokens expire after one hour, and a refresh token buys a new pair without asking for consent again. Refresh tokens rotate on every use, so the old one stops working the instant a new pair is issued. Two limits end a chain regardless: one unused for 30 days expires, and no chain outlives 90 days from its approval. After either, the app asks for consent again.