--- name: cms-export-partd-claims-ab2d description: >- Export Medicare Part A and B claims for a stand-alone Part D Prescription Drug Plan sponsor's active enrollees using the CMS AB2D API. Covers the Okta bearer-token exchange, the three live API versions, the Bulk FHIR kickoff-poll-download cycle, and the production IP-allowlisting precondition. api: CMS AB2D API (Claims Data to Part D Sponsors) spec: openapi/cms-ab2d-openapi.yml operations: - exportAllPatients - exportPatientsWithContract - getJobStatus - deleteRequest - downloadFile - capabilityStatement - getHealth - getMaintenanceMode generated: '2026-08-15' method: generated source: >- openapi/cms-ab2d-openapi.yml (harvested from https://sandbox.ab2d.cms.gov/v3/api-docs), https://ab2d.cms.gov/api-documentation --- # Export Part D sponsor claims from AB2D AB2D gives active stand-alone Prescription Drug Plan sponsors their enrollees' Part A and B claims, so they can see the medical picture behind the drug claims they already hold. Like BCDA it is Bulk FHIR: kickoff, poll, download. ## Preconditions - **Sandbox** (`https://sandbox.ab2d.cms.gov`) is open to anyone, uses synthetic claims, and authenticates against the sandbox identity provider `test.idp.idm.cms.gov`. - **Production** (`https://api.ab2d.cms.gov`) is restricted to PDP sponsors who have completed the production-access process, authenticates against `idm.cms.gov`, **and requires every calling system's static source IP address to be registered with CMS.** An unregistered client does not get a 401 — the connection simply does not complete. - Both environments expose the same endpoints. The only differences are the hostname and the identity provider. ## Versions The AB2D OpenAPI declares three FHIR generations in one document, all live: | Version | Path prefix | Patient export | Contract-scoped Group export | |---|---|---|---| | v3 (current) | `/api/v3/fhir` | `exportAllPatients` | not available | | v2 | `/api/v2/fhir` | `exportAllPatients_1` | `exportPatientsWithContract` | | v1 | `/api/v1/fhir` | `exportAllPatients_2` | `exportPatientsWithContract_1` | Target v3 unless you specifically need the contract-scoped Group export, which v3 dropped. The `_1` / `_2` suffixes are generated by the CMS toolchain, not chosen names — grep the spec before wiring them. ## Steps 1. **Get a bearer token.** Present your Okta credentials to the environment's identity provider. Tokens are **JWTs and expire after 30 minutes** — AB2D's own docs say to refresh at least every 30 minutes *during* a job, because jobs routinely outlive a single token. 2. **Check the API is up (optional).** `getHealth` — `GET /health`, or `getMaintenanceMode` — `GET /status`. AB2D is the only CMS API family that exposes machine-readable health, and the only one with a status page (). Check it before starting a long job. 3. **Read the CapabilityStatement (optional).** `capabilityStatement` — `GET /api/v3/fhir/metadata`. 4. **Kick off the export.** - `exportAllPatients` — `GET /api/v3/fhir/Patient/$export` for all your active enrollees. - `exportPatientsWithContract` — `GET /api/v2/fhir/Group/{contractNumber}/$export` to scope to one contract number (v2/v1 only). Send `Prefer: respond-async`. Success is **HTTP 202** with a `Content-Location` header carrying the job status URL; extract the job UUID from it. 5. **Handle 429 correctly.** A 429 means *too many jobs are currently running*, not too many requests per second. Either wait for a running job to finish or cancel one with `deleteRequest`. AB2D does not publish the concurrent-job ceiling. 6. **Poll for status.** `getJobStatus` — `GET /api/v3/fhir/Job/{jobUuid}/$status`. 202 with `X-Progress` while running; 200 with the file manifest on completion. Poll once every few minutes — AB2D's own guidance — because job duration scales with contract size and runs minutes to hours. 7. **Download the files.** `downloadFile` — `GET /api/v3/fhir/Job/{jobUuid}/file/{filename}`. Output is NDJSON: one FHIR `ExplanationOfBenefit` resource per line. Stream it. 8. **Cancel if needed.** `deleteRequest` — `DELETE /api/v3/fhir/Job/{jobUuid}/$status`. ## Filtering Use `_since` to pull incrementally and `_type` to restrict resource types; see . Query parameters differ between v1 and v2 — CMS publishes separate filtering pages per version for exactly this reason. ## Error handling FHIR `OperationOutcome` in `application/fhir+json` on every error. See `errors/cms-problem-types.yml`. The two you will actually hit are **401** (token older than 30 minutes) and **429** (a job is already running). ## Operations deliberately not used `createClient`, `udpateClient` (the typo is verbatim in the CMS spec), `getClient`, `enableClient`, `disableClient` and `createJobByContractOnBehalfOfClient` under `/api/v1/admin/` are CMS-internal administration of sponsor clients. They are not part of a sponsor's workflow.