Stitchflow
Epic logo

Epic User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 18, 2026

Summary and recommendation

Epic exposes a FHIR R4 API (base: https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4) secured via OAuth 2.0 SMART on FHIR, supporting both Authorization Code flow for user-facing apps and Backend Services (JWT client credentials) for system-to-system access.

Practitioner and PractitionerRole are the primary resources for clinical staff identity;

Patient covers patient portal users.

Production access requires App Orchard review, which can take weeks to months.

Integrating Epic into an identity graph requires careful handling of opaque, instance-specific FHIR IDs

they are base64-encoded, non-portable across environments, and must be resolved from external identifiers like NPI before they can anchor a reliable identity record.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (SMART on FHIR) — supports Authorization Code flow for user-facing apps and Backend Services (JWT client credentials) for system-to-system access
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: OAuth 2.0 (SMART on FHIR) - supports Authorization Code flow for user-facing apps and Backend Services (JWT client credentials) for system-to-system access

Setup steps

  1. Register your application in Epic's App Orchard (appmarket.epic.com) or via your organization's Epic instance.
  2. Specify redirect URIs, requested FHIR scopes, and app type (patient-facing, clinician-facing, or backend service).
  3. For Backend Services: generate an RSA or EC key pair, register the public JWK with Epic, and sign JWT assertions using the private key.
  4. For Authorization Code flow: redirect users to Epic's /oauth2/authorize endpoint; exchange the returned code for an access token at /oauth2/token.
  5. Include the access token as a Bearer token in the Authorization header of all API requests.
  6. Production access requires Epic review and approval via App Orchard; sandbox access is available at open.epic.com without approval.

Required scopes

Scope Description Required for
system/Practitioner.read Read Practitioner (clinical staff/user) records via backend service. Listing or reading clinician/staff user records in system context
system/Practitioner.write Write/update Practitioner records via backend service. Creating or updating clinician/staff user records
user/Practitioner.read Read Practitioner records in the context of an authenticated user. User-context read of clinician records
system/Patient.read Read Patient records via backend service. Reading patient user records
openid OpenID Connect scope for identity token issuance. OIDC-based SSO and identity verification
fhirUser Returns the FHIR resource URL of the authenticated user in the ID token. Identifying the logged-in user's FHIR resource

User object / data model

Field Type Description On create On update Notes
id string Epic-assigned FHIR logical ID for the Practitioner resource. server-assigned immutable Used as the resource identifier in all API calls.
identifier array of Identifier External identifiers such as NPI, DEA, or internal Epic user ID. optional supported system/value pairs; NPI uses system 'http://hl7.org/fhir/sid/us-npi'.
active boolean Whether the practitioner record is active. optional supported Setting to false deactivates the record.
name array of HumanName Practitioner's name(s); includes family, given, prefix, suffix. required supported use='official' for legal name.
telecom array of ContactPoint Contact details: phone, email, etc. optional supported system values: phone, email, fax.
address array of Address Practitioner's address(es). optional supported
gender code Administrative gender: male | female | other | unknown. optional supported
birthDate date Date of birth. optional supported
qualification array of Qualification Credentials and certifications (e.g., MD, RN). optional supported Includes code, period, and issuer.
communication array of CodeableConcept Languages the practitioner communicates in. optional supported
photo array of Attachment Image of the practitioner. optional supported Support varies by Epic instance.

Core endpoints

Read Practitioner by ID

  • Method: GET
  • URL: https://{epic-base}/api/FHIR/R4/Practitioner/{id}
  • Watch out for: The {id} is Epic's internal FHIR ID, not the NPI or login username. Use search by identifier to resolve from external IDs.

Request example

GET /api/FHIR/R4/Practitioner/eM5CWtq15N0WJeuCet5bJlQ3
Authorization: Bearer {access_token}

Response example

{
  "resourceType": "Practitioner",
  "id": "eM5CWtq15N0WJeuCet5bJlQ3",
  "active": true,
  "name": [{"use":"official","family":"Smith","given":["John"]}],
  "telecom": [{"system":"email","value":"jsmith@hospital.org"}]
}

Search Practitioners

  • Method: GET
  • URL: https://{epic-base}/api/FHIR/R4/Practitioner?family={name}&identifier={system}|{value}
  • Watch out for: Not all search parameters are supported on every Epic instance. Supported parameters vary by Epic version and configuration.

Request example

GET /api/FHIR/R4/Practitioner?family=Smith&_count=10
Authorization: Bearer {access_token}

Response example

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 2,
  "link": [{"relation":"next","url":"..."}],
  "entry": [{"resource":{"resourceType":"Practitioner","id":"abc123"}}]
}

Create Practitioner

  • Method: POST
  • URL: https://{epic-base}/api/FHIR/R4/Practitioner
  • Watch out for: Write access to Practitioner is not universally enabled; Epic instances must explicitly configure write permissions. Many Epic deployments are read-only via FHIR.

Request example

POST /api/FHIR/R4/Practitioner
Content-Type: application/fhir+json
{
  "resourceType":"Practitioner",
  "name":[{"use":"official","family":"Doe","given":["Jane"]}],
  "active":true
}

Response example

HTTP 201 Created
Location: /api/FHIR/R4/Practitioner/newId123
{
  "resourceType":"Practitioner",
  "id":"newId123"
}

Update Practitioner

  • Method: PUT
  • URL: https://{epic-base}/api/FHIR/R4/Practitioner/{id}
  • Watch out for: Full resource replacement (PUT) is required; partial updates via PATCH have limited support in Epic FHIR R4.

Request example

PUT /api/FHIR/R4/Practitioner/eM5CWtq15N0WJeuCet5bJlQ3
Content-Type: application/fhir+json
{
  "resourceType":"Practitioner",
  "id":"eM5CWtq15N0WJeuCet5bJlQ3",
  "active":false
}

Response example

HTTP 200 OK
{
  "resourceType":"Practitioner",
  "id":"eM5CWtq15N0WJeuCet5bJlQ3",
  "active":false
}

Read PractitionerRole (role/department assignment)

  • Method: GET
  • URL: https://{epic-base}/api/FHIR/R4/PractitionerRole?practitioner={id}
  • Watch out for: PractitionerRole is the correct resource for department/specialty/location assignments; role data is not embedded in the Practitioner resource itself.

Request example

GET /api/FHIR/R4/PractitionerRole?practitioner=eM5CWtq15N0WJeuCet5bJlQ3
Authorization: Bearer {access_token}

Response example

{
  "resourceType":"Bundle",
  "entry":[{"resource":{
    "resourceType":"PractitionerRole",
    "practitioner":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3"},
    "organization":{"reference":"Organization/org1"},
    "code":[{"text":"Physician"}]
  }}]
}

Read Patient (patient user record)

  • Method: GET
  • URL: https://{epic-base}/api/FHIR/R4/Patient/{id}
  • Watch out for: Patient records represent patient portal users, not clinical staff. MyChart (patient portal) account status is not directly exposed via FHIR.

Request example

GET /api/FHIR/R4/Patient/erXuFYUfucBZaryVksYEcMg3
Authorization: Bearer {access_token}

Response example

{
  "resourceType":"Patient",
  "id":"erXuFYUfucBZaryVksYEcMg3",
  "active":true,
  "name":[{"use":"official","family":"Doe","given":["Jane"]}],
  "telecom":[{"system":"email","value":"jane@example.com"}]
}

Token endpoint (OAuth 2.0 Backend Services)

  • Method: POST
  • URL: https://{epic-base}/oauth2/token
  • Watch out for: JWT must be signed with the private key whose public JWK was registered during app setup. The 'sub' and 'iss' claims must match the registered client_id.

Request example

POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion={signed_jwt}

Response example

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "system/Practitioner.read"
}

Rate limits, pagination, and events

  • Rate limits: Epic does not publicly document specific numeric rate limits in its open developer documentation. Rate limiting behavior is instance-specific and negotiated per organization or app registration. The open sandbox (open.epic.com) may impose undocumented throttling.

  • Rate-limit headers: No

  • Retry-After header: No

  • Rate-limit notes: No publicly documented rate limit headers or Retry-After behavior found in official Epic FHIR documentation. Contact Epic or your Epic technical account manager for production rate limit details.

  • Pagination method: token

  • Default page size: 10

  • Max page size: Not documented

  • Pagination pointer: next (link relation in FHIR Bundle response)

  • Webhooks available: No

  • Webhook notes: Epic does not offer a native outbound webhook system for user/practitioner change events via its public FHIR API. Epic's FHIR Subscriptions (R4) support is limited and instance-specific.

  • Alternative event strategy: Use FHIR Subscription resource (where supported by the Epic instance) or poll the Practitioner/Patient search endpoints periodically to detect changes. Some Epic integrations use HL7 ADT/MDM messages via Epic's integration engine (Bridges) for event-driven workflows.

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • Epic does not expose a native SCIM 2.0 endpoint for user provisioning.
  • User provisioning into Epic is typically handled via Epic's internal Identity Management tools, HL7 interfaces, or custom integrations built on Epic's APIs.
  • Some organizations use third-party middleware (e.g., Okta, Azure AD) with custom Epic FHIR-based connectors, but these are not native SCIM implementations.
  • Enterprise-tier customers may work with Epic's professional services for custom provisioning workflows.

Common scenarios

Three scenarios cover the core provisioning surface.

First, clinician lookup by NPI: obtain a backend service token with system/Practitioner.read, search GET /api/FHIR/R4/Practitioner?identifier=http://hl7.org/fhir/sid/us-npi|{npi_value}, extract the FHIR id from the Bundle, then fetch PractitionerRole for department and specialty assignments

NPI search support must be confirmed per instance.

Second, practitioner deactivation: read the current resource, set active=false, and submit a full PUT replacement with system/Practitioner.write scope;

partial PATCH has limited R4 support.

Third, full roster pagination: issue GET /api/FHIR/R4/Practitioner?_count=50, follow the link[relation=next] URL in each Bundle response until no next link is returned

do not reconstruct the next URL manually, as it is opaque and instance-specific.

Look up a clinician's profile and role by NPI

  1. Obtain a backend service access token via POST to /oauth2/token with a signed JWT client assertion and system/Practitioner.read scope.
  2. Search for the Practitioner by NPI: GET /api/FHIR/R4/Practitioner?identifier=http://hl7.org/fhir/sid/us-npi|{npi_value}
  3. Extract the Practitioner FHIR id from the Bundle entry.
  4. Fetch PractitionerRole: GET /api/FHIR/R4/PractitionerRole?practitioner={fhir_id} to retrieve department, specialty, and organization assignments.

Watch out for: NPI search support must be confirmed with the specific Epic instance; not all identifier search parameters are enabled universally.

Deactivate a practitioner record

  1. Obtain a backend service access token with system/Practitioner.write scope.
  2. Read the current Practitioner resource: GET /api/FHIR/R4/Practitioner/{id}.
  3. Modify the 'active' field to false in the retrieved resource body.
  4. Submit a full resource replacement: PUT /api/FHIR/R4/Practitioner/{id} with the updated resource.
  5. Verify the response returns HTTP 200 with active=false.

Watch out for: Write access must be explicitly enabled on the Epic instance. Deactivating a FHIR Practitioner record does not automatically revoke Epic application login access - that requires action within Epic's internal security administration.

Paginate through all Practitioners in an organization

  1. Obtain a backend service access token with system/Practitioner.read scope.
  2. Issue initial search: GET /api/FHIR/R4/Practitioner?_count=50
  3. Parse the returned FHIR Bundle; check 'link' array for a relation='next' URL.
  4. Follow the 'next' URL to retrieve the subsequent page.
  5. Repeat until no 'next' link is present in the Bundle response.

Watch out for: The 'next' link URL is opaque and instance-specific; do not reconstruct it manually. Default and maximum page sizes vary by Epic instance and are not publicly documented.

Why building this yourself is a trap

The most significant caveat in Epic's FHIR API is scope: it manages clinical identity records, not Epic application access. Deactivating a Practitioner resource via FHIR does not revoke Epic login credentials or security class assignments - those live in Epic's internal EMP/SER records and are only accessible through Hyperspace administration.

Write operations (POST/PUT on Practitioner) are disabled by default on most instances and require explicit configuration by an Epic administrator. Epic does not expose a native SCIM 2.0 endpoint; provisioning into Epic's access model requires HL7 interfaces, Epic's internal Identity Management tooling, or custom middleware.

Rate limits are instance-specific and not publicly documented - contact your Epic technical account manager for production thresholds. SMART on FHIR scopes must be pre-approved at app registration; requesting unapproved scopes at runtime returns an error with no override path.

Automate Epic workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 18, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste