SMART Backend Services is how a server, or an AI agent's tool layer, calls a FHIR API with no user present: register a public key (JWKS), sign a JWT that expires within 5 minutes, exchange it for a token that should live at most 300 seconds, and request narrow SMART v2 system/ scopes, never system/*.cruds.
Backend services used to be a niche: nightly exports and population analytics. In 2026 they are on most integration roadmaps, because AI agents, prior-authorization automation and payer-provider data exchange all need server-to-server access. ONC's HTI-1 rule made SMART App Launch 2.0.0 the floor for certified (g)(10) APIs from December 31, 2025, and the current published version of the spec is SMART App Launch v2.2.0. This guide is for engineers and CTOs who have to get it right: the protocol, the scope design, key management, and the agent-specific failure modes.
It is the SMART profile for clients that run "autonomously (or semi-autonomously)" with access pre-authorized by the FHIR server. It uses the OAuth 2.0 client-credentials grant with asymmetric (public key) client authentication. There is no user login and no redirect.
The spec says it fits when the client runs without user-level authorization, supports asymmetric authentication, and there is "no compelling need" for a user to authorize access at runtime. Typical uses: bulk export, data pipelines, scheduled jobs, and the server side of an AI agent that works on queues rather than on behalf of a signed-in clinician.
| EHR launch / standalone launch | Backend Services | |
|---|---|---|
| Who authorizes | A user (clinician or patient) at runtime | The FHIR server's administrator, in advance |
| Grant | Authorization code (+ PKCE) | Client credentials with a signed JWT assertion |
| Typical scopes | user/ or patient/ | system/ |
| Audit attribution | The signed-in user | The client (often mapped to a service user by the EHR) |
| Best for | Interactive apps and assistants in a clinician's workflow | Batch jobs, exports, queue workers, agent tool layers |
Four steps: discover the server's .well-known/smart-configuration, build and sign an authentication JWT with your private key, POST it to the token endpoint with grant_type=client_credentials, and call the FHIR API with the short-lived bearer token you get back.
token_endpoint, token_endpoint_auth_methods_supported (must include private_key_jwt) and token_endpoint_auth_signing_alg_values_supported (must include at least one of RS384 or ES384), per the asymmetric client authentication page.alg, kid (unique within your JWK Set), typ: JWT, optional jku. Claims: iss and sub = your client_id, aud = the token URL, exp no more than five minutes in the future, and a unique jti.scope, grant_type=client_credentials, client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and client_assertion.access_token, token_type: bearer, expires_in and scope. The granted scope "can be different from the scopes requested," so check it before you run the job.On lifetime, the spec is explicit: access tokens "SHALL be short-lived; the expires_in value SHOULD NOT exceed 300," which is five minutes. Request new tokens as needed instead of caching one for an hour.

Use ES384 or RS384, published through a JWKS URL with a stable kid per key, separate key pairs per environment, and a rotation process that adds the new key before using it. Check each EHR's own rules, because they differ.
The SMART spec requires servers to support at least one of RS384 or ES384. EHR implementations add their own constraints. For example, Epic's OAuth 2.0 specification accepts RS256, RS384 and RS512 for all confidential clients and ES256 and ES384 only for clients that register a JWK Set URL, requires jti of at most 151 characters, and will not accept a non-production JWKS URL as the production URL for new apps.
exp, clock skew of a few minutes breaks auth intermittently. Run NTP and log the server's error body.
Request the smallest set of resource-level scopes using v2 syntax (create, read, update, delete, search), split read and write into different clients where you can, and use granular query-parameter scopes only where the server documents support for them.
From the SMART scopes specification:
system/Observation.rs means read and search Observations. v1 scopes map as .read → .rs, .write → .cud, .* → .cruds.patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory for lab results only._filter in scopes as experimental "even if they are supported by a server." Do not build your security model on them.| Client | Scopes | Why separate |
|---|---|---|
| Reader (pipelines, agent context) | system/Patient.rs system/Encounter.rs system/Condition.rs system/Observation.rs | Most jobs only read; a leak exposes data but cannot change it |
| Writer (approved actions only) | system/DocumentReference.c system/Task.cu | Write path is small, audited, and only called after human approval |
| Bulk exporter | Resource-level .rs scopes for the exported types | Long-running jobs; isolate rate limits and keys |
Resource names here are illustrative. Use what each EHR documents as supported for system/ access, which varies by vendor and version.
Use user-context tokens (EHR launch) when the agent acts for a signed-in clinician, so access and audit follow that person. Use Backend Services for queue-driven work that no single user triggers, and keep the token in a tool layer the model cannot see.
The risk with backend tokens for agents is simple: a system/ token sees every patient the client is pre-authorized for, and the EHR's audit log attributes calls to the client, not to the person who asked the agent a question. Prompt injection or a planning error then turns into a population-level data exposure. Controls we put in front of every backend token an agent can reach:
If you expose FHIR tools to agents through MCP, the same rules apply to the MCP server: it holds the credentials, enforces the allow-list and logs every call. See MCP server development and our AI agent security checklist.
Clock skew, reused jti values, wrong aud, JWKS caching surprises during rotation, assuming the granted scope equals the requested scope, and caching access tokens past their lifetime. Each produces intermittent 401s that are hard to reproduce.
aud mismatch: it must be the token endpoint URL. Some servers allow extra audiences; Epic, for example, lets customer administrators add audience URLs to an allow-list.jti reuse: the server "SHALL" check that a jti has not been seen for the same issuer within the JWT lifetime. Retrying a failed request with the same JWT fails for this reason. Generate a new JWT per attempt.scope may be narrower than requested. Fail closed if a required scope is missing.Test the unhappy paths deliberately: expired JWT, reused jti, wrong kid, rotated key, narrowed scope, revoked client, and 429 from the token endpoint. Then run the same suite against each customer's non-production environment, not just the vendor sandbox.
For AI features, add an evaluation set that proves the agent never requests data outside the bound patient, never calls a write tool without an approval record, and degrades safely when the token broker refuses. That evaluation belongs in CI, not in a one-time pen test. Our guide on LLM testing for regulated industries covers how we build those suites. For bulk exports built on the same auth, see bulk FHIR export for analytics and AI.
No. It is an authentication and authorization mechanism. HIPAA still requires a BAA with every business associate handling PHI, access controls, audit controls and minimum-necessary use, and those sit in your architecture, not in the token.
A well-scoped backend client helps you show the minimum-necessary and access-control parts. You still need logging, retention, incident response and vendor agreements for your cloud, model provider and anyone else in the data path. See HIPAA-compliant AI agents for the full set of controls.
We build backend integrations the way a security reviewer expects: JWKS-published keys held in a cloud KMS, per-environment key pairs, scripted rotation, split reader and writer clients, a token broker in front of any AI component, and an audit trail that names the human behind every agent action. We test the failure paths in CI and against your customers' non-production environments. BeevR signs a BAA as a business associate, AI models can run inside your own AWS account under the AWS BAA, and you own the source code and IP from day one.
Our senior, founder-led team in Hanoi has a multi-tenant EHR live across four US clinics at about 300 patients a day, and you see a working demo every Friday. Work runs in fixed-scope, fixed-price phases from $10K per phase. See our EHR integration services and HIPAA-compliant AI agent work.
Connecting a server or an AI agent to an EHR? Book a 30-minute call and we will review your auth flow and scope design with you.