By EHR

athenahealth bulk FHIR export for research.

Clinical Extract connects to athenahealth as a backend app that signs its own token requests and exports the study cohort’s Group through athenaOne’s certified FHIR R4 API. Only the variables your protocol approved reach study.csv, and a data dictionary describes each one.

1 Works with athenahealth

athenahealth support through athenaOne.

athenahealth’s certified API (ONC §170.315(g)(10)) exports a Group of patients through HL7 FHIR Bulk Data Access (Bulk Data 1.0.1 and 2.0.0), authorized with SMART Backend Services. Clinical Extract uses that exchange end to end: the token request at athenahealth’s OAuth endpoint, the Group kickoff, the status poll and the file downloads, read-only, in your environment. Your practice ID identifies your practice, and the export runs against it alone.

athenahealth data extraction for a registry or a study runs as one Group export per cohort, whether you built the cohort from coded criteria or matched it from a patient list, and it reads only the resource types the protocol names.

The study cohort as NDJSON, flattened to CSV
Patient, Encounter, Condition, Observation, MedicationRequest and Procedure files land in your study folder. Clinical Extract flattens the approved variables into study.csv and writes a data-dictionary.json entry for every column.
Coded columns
Diagnoses, results, medications and procedures come out as ICD-10-CM, LOINC, RxNorm and SNOMED CT codes, each column traced to its FHIR element.
The oncology lens
ER, PR, HER2, PD-L1, EGFR, KRAS, BRAF, ALK, MSI and TNM clinical and pathologic arrive as named columns.
Roster pull and the cohort builder
Match a patient list against the practice with graded results, or build the cohort from coded criteria and read the feasibility count before the export.

2 Connecting

Connecting with private_key_jwt on athenaOne.

The athenahealth setup has three steps.

  1. Register. With athenahealth, Clinical Extract is a backend app with system access. Its client authentication is private_key_jwt with Clinical Extract’s public key, its scopes are one system/<Resource>.read scope per exported type plus system/Group.read, and athenahealth issues the client ID.
  2. Activate. Your practice enables the app, your practice ID identifies it, and you name the study Group.
  3. Export. Clinical Extract signs a JWT with the private key that stays in your environment, exchanges it at /oauth2/v1/token for an access token with read scopes only, and runs the Group export on /fhir/r4.

Our team also builds interfaces for athenahealth practices.

Connecting Clinical Extract to athenahealthThree numbered steps from left to right. Step 1, register, at athenahealth (app registration), outside your environment: Application: backend app, system access; Client authentication: private_key_jwt, with Clinical Extract’s public key; System scopes: system/Patient.read, system/Group.read, system/Encounter.read, system/Condition.read, system/Observation.read, system/MedicationRequest.read, system/Procedure.read; Issued: client ID. An arrow carries the client ID to step 2. Step 2, activate, at Your athenaOne (your practice): Application: enabled for your practice; Practice ID: identifies your practice; FHIR base: api.platform.athenahealth.com, /fhir/r4; Group: the study cohort, by Group ID. An arrow carries the base URL and Group ID to step 3. Step 3, export, from Clinical Extract in your environment: request POST …/oauth2/v1/token (grant_type=client_credentials, client_assertion: JWT signed RS384, scope=system/Patient.read …); request GET …/fhir/r4/Group/{id}/$export (?_type=Patient,Encounter,Condition,Observation,MedicationRequest,Procedure, Prefer: respond-async), highlighted; response 202 Accepted (Content-Location: {status-url}); request GET {status-url} (202 Accepted while the export runs, with Retry-After); response 200 OK, JSON manifest (transactionTime, request,, requiresAccessToken, error[],, output[{type, url, count}]); request GET {output-url} (NDJSON, one resource per line); result study.csv, data-dictionary.json (flattened, approved variables only).1REGISTERathenahealthapp registrationAPPLICATIONbackend app, system accessCLIENT AUTHENTICATIONprivate_key_jwtwith Clinical Extract’s public keySYSTEM SCOPESsystem/Patient.readsystem/Group.readsystem/Encounter.readsystem/Condition.readsystem/Observation.readsystem/MedicationRequest.readsystem/Procedure.readISSUEDclient ID2ACTIVATEYour athenaOneyour practiceAPPLICATIONenabled for your practicePRACTICE IDidentifies your practiceFHIR BASEapi.platform.athenahealth.com/fhir/r4GROUPthe study cohort, by Group ID3EXPORTClinical Extractin your environmentrequestresponsePOST …/oauth2/v1/tokengrant_type=client_credentialsclient_assertion: JWT signed RS384scope=system/Patient.read …GET …/fhir/r4/Group/{id}/$export?_type=Patient,Encounter,Condition,Observation,MedicationRequest,ProcedurePrefer: respond-async202 AcceptedContent-Location: {status-url}GET {status-url}202 Accepted while the exportruns, with Retry-After200 OK, JSON manifesttransactionTime, request,requiresAccessToken, error[],output[{type, url, count}]GET {output-url}NDJSON, one resource per linestudy.csv, data-dictionary.jsonflattened, approved variables onlyclient IDbase URLGroup ID
Connecting Clinical Extract to athenahealthThree numbered steps from left to right. Step 1, register, at athenahealth (app registration), outside your environment: Application: backend app, system access; Client authentication: private_key_jwt, with Clinical Extract’s public key; System scopes: system/Patient.read, system/Group.read, system/Encounter.read, system/Condition.read, system/Observation.read, system/MedicationRequest.read, system/Procedure.read; Issued: client ID. An arrow carries the client ID to step 2. Step 2, activate, at Your athenaOne (your practice): Application: enabled for your practice; Practice ID: identifies your practice; FHIR base: api.platform.athenahealth.com, /fhir/r4; Group: the study cohort, by Group ID. An arrow carries the base URL and Group ID to step 3. Step 3, export, from Clinical Extract in your environment: request POST …/oauth2/v1/token (grant_type=client_credentials, client_assertion: JWT signed RS384, scope=system/Patient.read …); request GET …/fhir/r4/Group/{id}/$export (?_type=Patient,Encounter,Condition,Observation,MedicationRequest,Procedure, Prefer: respond-async), highlighted; response 202 Accepted (Content-Location: {status-url}); request GET {status-url} (202 Accepted while the export runs, with Retry-After); response 200 OK, JSON manifest (transactionTime, request,, requiresAccessToken, error[],, output[{type, url, count}]); request GET {output-url} (NDJSON, one resource per line); result study.csv, data-dictionary.json (flattened, approved variables only).1REGISTERathenahealthapp registrationAPPLICATIONbackend app, system accessCLIENT AUTHENTICATIONprivate_key_jwtwith Clinical Extract’s public keySYSTEM SCOPESsystem/Patient.readsystem/Group.readsystem/Encounter.readsystem/Condition.readsystem/Observation.readsystem/MedicationRequest.readsystem/Procedure.readISSUEDclient IDclient ID2ACTIVATEYour athenaOneyour practiceAPPLICATIONenabled for your practicePRACTICE IDidentifies your practiceFHIR BASEapi.platform.athenahealth.com/fhir/r4GROUPthe study cohort, by Group IDbase URLGroup ID3EXPORTClinical Extractin your environmentrequestresponsePOST …/oauth2/v1/tokengrant_type=client_credentialsclient_assertion: JWT signed RS384scope=system/Patient.read …GET …/fhir/r4/Group/{id}/$export?_type=Patient,Encounter,Condition,Observation,MedicationRequest,ProcedurePrefer: respond-async202 AcceptedContent-Location: {status-url}GET {status-url}202 Accepted while the export runs, withRetry-After200 OK, JSON manifesttransactionTime, request,requiresAccessToken, error[],output[{type, url, count}]GET {output-url}NDJSON, one resource per linestudy.csv, data-dictionary.jsonflattened, approved variables only
Figure 1. Connecting Clinical Extract to athenahealth. Clinical Extract is registered once with athenahealth as a backend app that signs its token requests with its own key; your practice enables it; Clinical Extract then runs the Group export on athenahealth’s certified API, in your environment. Resource lists and URLs are abridged.

3 Export time

How long an athenahealth export runs.

Because the request names one Group and the protocol’s resource types, an athenahealth export for a study finishes in hours. Clinical Extract waits the interval each Retry-After header gives, then downloads every file the manifest lists with the bearer token and writes the CSV and its dictionary.

Every header of the kickoff, status and manifest exchange is in our guide to bulk FHIR export.

4 Facts

athenahealth connection facts.

Table 1. Endpoint, registration, authentication, export type and output on athenahealth. Checked September 2026.
Endpoint FHIR R4 base https://api.platform.athenahealth.com/fhir/r4; your practice identified by its practice ID
Registration athenahealth app registration: a backend app with system access, client authentication private_key_jwt with Clinical Extract’s public key, one system/<Resource>.read scope per exported type plus system/Group.read; a client ID
Authentication SMART Backend Services: client_credentials with a JWT signed RS384 by Clinical Extract’s private key, exchanged at https://api.platform.athenahealth.com/oauth2/v1/token for an access token with one system/<Resource>.read scope per exported type plus system/Group.read
Export type Group export: GET /fhir/r4/Group/{group-id}/$export with _type (Bulk Data 1.0.1 and 2.0.0); 202 Accepted with a Content-Location status URL
Status and files 202 with Retry-After while the export runs; 200 with the JSON manifest; NDJSON files fetched with the bearer token, one resource per line
Output study.csv (UTF-8, one row per cohort patient, one column per approved variable) with data-dictionary.json (description, fhirSource and example per column); the NDJSON files and the manifest kept beside them in your study folder

The table scrolls sideways.

Sources: athenahealth API documentation, HL7 Bulk Data Access, SMART Backend Services.

5 Specimens

What the exchange and the output look like.

Every value is synthetic. The cohort is a 212-patient breast cancer study with 24 approved variables; the manifest counts and the row shown come from it.

Listing 1. The export kickoff: one request for the study cohort's Group and the resource types the protocol names, answered at once with the status URL.
GET https://api.platform.athenahealth.com/fhir/r4/Group/{group-id}/$export
    ?_type=Patient,Encounter,Condition,Observation,MedicationRequest,Procedure
Accept: application/fhir+json
Prefer: respond-async
Authorization: Bearer {access token}

202 Accepted
Content-Location: https://api.platform.athenahealth.com/fhir/r4/{status-path}
Listing 2. The completed status response: the manifest lists one NDJSON file per requested type with its line count, and says the files need the bearer token.
{
  "transactionTime": "2026-09-28T14:02:11Z",
  "request": "https://api.platform.athenahealth.com/fhir/r4/Group/{group-id}/$export?_type=Patient,Encounter,Condition,Observation,MedicationRequest,Procedure",
  "requiresAccessToken": true,
  "output": [
    { "type": "Patient", "url": "https://api.platform.athenahealth.com/fhir/r4/{file-path-1}", "count": 212 },
    { "type": "Encounter", "url": "https://api.platform.athenahealth.com/fhir/r4/{file-path-2}", "count": 3120 },
    { "type": "Condition", "url": "https://api.platform.athenahealth.com/fhir/r4/{file-path-3}", "count": 1684 },
    { "type": "Observation", "url": "https://api.platform.athenahealth.com/fhir/r4/{file-path-4}", "count": 48310 },
    { "type": "MedicationRequest", "url": "https://api.platform.athenahealth.com/fhir/r4/{file-path-5}", "count": 3905 },
    { "type": "Procedure", "url": "https://api.platform.athenahealth.com/fhir/r4/{file-path-6}", "count": 912 }
  ],
  "error": []
}
Listing 3. One line of Observation.ndjson: a complete FHIR resource per line, here the estrogen receptor status of patient eKx3p9Qa (LOINC 16112-5; SNOMED CT 10828004, Positive).
{"resourceType":"Observation","id":"eObs-3f9a","status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"16112-5","display":"Estrogen receptor [Interpretation] in Tissue"}]},"subject":{"reference":"Patient/eKx3p9Qa"},"effectiveDateTime":"2024-03-20","valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"10828004","display":"Positive"}]}}
Listing 4. study.csv, the header row and row 1 of 212: one row per patient in the cohort, one column per approved variable (10 of 24 columns shown). Values are synthetic.
patient_ref,birth_year,gender,dx_code,dx_date,stage_group,er_status,pr_status,her2_status,first_chemo
eKx3p9Qa,1961,female,C50.412,2024-03-14,IIA,Positive,Positive,Negative,paclitaxel
Listing 5. The er_status entry in data-dictionary.json: the description with its code system, the FHIR element the value came from, and an example value.
{
  "er_status": {
    "description": "Estrogen receptor status (LOINC 16112-5)",
    "fhirSource": "Observation.valueCodeableConcept",
    "example": "Positive"
  }
}

6 Questions

Questions about athenahealth

Does athenahealth support bulk FHIR export?

athenahealth is certified to ONC §170.315(g)(10), and its FHIR R4 API exposes the Group export with backend-services authorization. Clinical Extract runs it as a backend app for your practice.

Is athenaOne the same as athenahealth here?

athenaOne is the athenahealth EHR that the certified FHIR R4 API belongs to. Your practice ID identifies your practice in every request.

What does our practice enable?

Your practice enables the backend app one time. For each study you name a Group, and Clinical Extract exports it.

What lands in the study folder?

study.csv, with a row per patient in the cohort and a column per approved variable, plus data-dictionary.json, the manifest and the NDJSON files the export returned.

Next

See Clinical Extract run on one of your studies.

Tell us which EHR you run and what the study or registry needs. We reply within one business day to set a meeting time.

Request a demo