Exchange Payer-to-Payer Member Data (PDex)
Match a member against their prior health plan, then export that member's payer-to-payer clinical and claims data with Sandbox endpoints, view request/response shapes, and error handling expectations.
When a member switches health plans, CMS-0057-F requires the new plan to be able to pull that member's clinical and claims history from their prior plan. This guide covers b.well's implementation of that payer-to-payer exchange: your health plan calls b.well's CMS Network to match the member against their old plan, then export that member's data from it.
Two steps, each an async submit-then-poll pair:
| Step | What you do | Endpoint |
|---|---|---|
| 1 | Submit a member match, then poll for the matched Group | POST .../fhir/r4/Group/$bulk-member-match → GET .../fhir/r4/Group/$bulk-member-match/{job_id} |
| 2 | Submit a data export for that Group, then poll for the manifest | POST .../fhir/r4/Group/{group_id}/$davinci-data-export → GET .../fhir/r4/Group/{group_id}/$davinci-data-export |
Step 1 is not optionalStep 2's
{group_id}must come from a Group a prior member match returned. b.well rejects an export request against a Group id it didn't just hand you.
Before you begin
b.well issues these to your organization during onboarding:
- OAuth client id and client secret, per environment
- Your organization's scope for PDex payer-to-payer access
Begin in SandboxExamples below use the Sandbox base URL directly. Substitute your own client id, client secret, and access token — all example data is fictional.
Base URL
| Environment | Base URL |
|---|---|
| Sandbox | https://partner-api.client-sandbox.icanbwell.com |
b.well provides two environments for API access: Sandbox (non-production) and Production. Each environment has dedicated endpoints for both GraphQL and REST APIs
Production Environment
Provided during onboarding
Authenticate
curl --request POST \
--url https://auth.partner-api.client-sandbox.icanbwell.com/oauth2/v1/apps/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=<your_client_id> \
--data client_secret=<your_client_secret> \
--data resource=https://partner-api.client-sandbox.icanbwell.comResponse — 200:
{
"access_token": "{jwt}",
"token_type": "Bearer",
"expires_in": 3600
}Use access_token as Authorization: Bearer {access_token} on every call below. Tokens last 3600 seconds; re-authenticate on a 401 rather than caching one for the life of a batch job.
Step 1 — Submit a member match
curl -X POST 'https://partner-api.client-sandbox.icanbwell.com/fhir/r4/Group/$bulk-member-match' \
-H "Authorization: Bearer <access_token>" \
-H "Prefer: respond-async" \
-H "Content-Type: application/json" \
-d '@member-match.json'Request body — a FHIR Parameters resource per the Da Vinci PDex multi-member-match profile:
{
"resourceType": "Parameters",
"id": "91c52548-8845-42d8-9473-70e1b7cb6c1d",
"meta": {
"profile": ["http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/pdex-parameters-multi-member-match-bundle-in"]
},
"parameter": [{
"name": "MemberBundle",
"part": [
{
"name": "MemberPatient",
"resource": {
"resourceType": "Patient",
"id": "patient-1",
"identifier": [{
"type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "MB"}]},
"system": "http://example.org/old-payer/identifiers/member",
"value": "55678",
"assigner": {"display": "Old Payer"}
}],
"name": [{"use": "official", "family": "Person", "given": ["Patricia", "Ann"]}],
"gender": "female",
"birthDate": "1974-12-25"
}
},
{
"name": "CoverageToMatch",
"resource": {
"resourceType": "Coverage",
"id": "coverage-1",
"status": "draft",
"subscriberId": "1234564321",
"beneficiary": {"reference": "Patient/1"},
"relationship": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", "code": "self"}]},
"payor": [{"identifier": {"system": "http://hl7.org/fhir/sid/us-npi", "value": "9876543210"}, "display": "Old Health Plan"}]
}
},
{
"name": "Consent",
"resource": {
"resourceType": "Consent",
"id": "consent-1",
"status": "active",
"scope": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/consentscope", "code": "patient-privacy"}]},
"category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "IDSCL"}]}],
"patient": {"reference": "Patient/1"},
"provision": {
"type": "permit",
"action": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/consentaction", "code": "disclose"}]}]
}
}
},
{
"name": "CoverageToLink",
"resource": {
"resourceType": "Coverage",
"id": "coverage-link-1",
"status": "draft",
"subscriberId": "432156789",
"beneficiary": {"reference": "Patient/1"},
"payor": [{"identifier": {"system": "http://hl7.org/fhir/sid/us-npi", "value": "0123456789"}, "display": "New Health Plan"}]
}
}
]
}]
}Response — 202 Accepted, empty body:
Content-Location: https://partner-api.client-sandbox.icanbwell.com/fhir/r4/Group/$bulk-member-match/{job_id}Errors
| Status | When | Body |
|---|---|---|
401 | Missing, invalid, or expired bearer token | OperationOutcome, issue code security |
403 | Token or caller isn't scoped for PDex | OperationOutcome, issue code forbidden |
5XX | b.well-side issue | OperationOutcome, issue code transient — retry after a delay |
Step 2 — Poll the member match
curl 'https://partner-api.client-sandbox.icanbwell.com/fhir/r4/Group/$bulk-member-match/{job_id}' \
-H "Authorization: Bearer <access_token>"{job_id} comes from Step 1's Content-Location header.
Response — 200. Body shape follows the $multi-member-match payer example response in the Da Vinci PDex v2.1.0 spec.
Errors: same 401 / 403 shapes as Step 1.
Step 3 — Submit a data export
curl -X POST 'https://partner-api.client-sandbox.icanbwell.com/fhir/r4/Group/{group_id}/$davinci-data-export?exportType=hl7.fhir.us.davinci-pdex%23payertopayer' \
-H "Authorization: Bearer <access_token>" \
-H "Prefer: respond-async"{group_id} must be a Group id returned by Step 2 — b.well rejects export requests against any other Group id.
exportType is required and must equal exactly hl7.fhir.us.davinci-pdex#payertopayer, URL-encoded as %23 for #. Any other value fails validation.
Optional query params _since, _until, _typeFilter, and _type are accepted and persisted, not yet format-validated.
No request body.
Response — 202 Accepted, empty body:
Content-Location: https://partner-api.client-sandbox.icanbwell.com/fhir/r4/Group/{group_id}/$davinci-data-export?exportType=hl7.fhir.us.davinci-pdex%23payertopayer
Content-Locationworks differently hereUnlike Steps 1–2, this is the literal request URL — including your query string — echoed back, not a separate job-id path.
Errors
| Status | When |
|---|---|
401 | Missing, invalid, or expired bearer token |
403 | Caller isn't PDex-enabled |
404 | {group_id} doesn't resolve to a real Group |
503 | b.well-side issue — retry after a delay |
Step 4 — Poll and retrieve the data export
curl 'https://partner-api.client-sandbox.icanbwell.com/fhir/r4/Group/{group_id}/$davinci-data-export' \
-H "Authorization: Bearer <access_token>"Response — 200:
{
"transactionTime": "2027-01-04T18:22:11.000Z",
"request": "/Group/{group_id}/$davinci-data-export",
"requiresAccessToken": false,
"output": [
{ "type": "Claim", "url": "https://example.com/claim.ndjson", "count": 20 }
],
"error": []
}Each entry in output is one resource type's NDJSON file. Fetch each url directly.requiresAccessToken tells you whether that fetch also needs your bearer token.
Errors: 401 / 403 (same scopes as Step 3), 404 if {group_id} doesn't resolve.
Errors
| Status | Meaning |
|---|---|
401 | Token missing, invalid, or expired. Re-authenticate and retry. |
403 | Your credentials aren't scoped for PDex payer-to-payer. Contact b.well. |
404 | The {group_id} in your request doesn't resolve to a real Group. Confirm it came from a member match response. |
5XX / 503 | b.well-side issue. Retry after a delay. |
Updated about 7 hours ago
