---
title: "API authentication reference - bearer tokens"
description: "How the HyperCRM API authenticates requests: the bearer token header, Firebase ID tokens versus OAuth access tokens, and the auth error codes."
canonical: "https://hypercrm.app/docs/api/authentication"
lang: "en"
updated: "2026-07-25"
---

_API reference / Authentication_

# Bearer tokens, in detail

Every 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.

[Start the free trial](/dashboard) · [Back to API reference](/docs/api)

## The header

Every endpoint requires `Authorization: Bearer <token>`. There are two valid kinds of token: a Firebase ID token from a signed-in HyperCRM session, or an opaque OAuth access token (`hcrm_at_...`) issued by HyperCRM's own OAuth 2.1 authorization server for the MCP endpoint. Both are accepted wherever a bearer token is expected — the server checks whether the token starts with `hcrm_at_` and, if not, falls back to Firebase ID token verification.

## 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, practice, and owner membership row. Every subsequent call resolves the same practice from the token — 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.

## 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 four 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` or `user_disabled` depending on which side flagged it. `unsupported_auth_provider` and `auth_provider_email_required` (both 403) cover sign-in providers HyperCRM doesn't support for API access. `auth_configuration_missing` (503) means the server itself has no Firebase project configured.

See the [full error reference](/docs/api/errors) for how these codes fit into the overall error envelope, and the [API reference hub](/docs/api) for the other resource pages.

## Common questions

### How is patient data protected?

Patient records are encrypted in transit and at rest, files are stored privately and served through short-lived signed links, and every change is written to an audit log. Staff accounts sign in with passkeys rather than shared passwords.

### Can I authenticate without a Firebase-signed-in session?

Yes, 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.

### Why does a disabled account return 403 instead of 401?

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.

### Do access tokens expire, and can they be refreshed?

Yes. OAuth access tokens are short-lived, and a refresh token can be exchanged for a new access/refresh pair without the user approving consent again. Refresh tokens rotate on every use, so the previous refresh token stops working the instant a new pair is issued, which limits the damage of a leaked token.