Skip to main content
The Sandbox is the platform’s FHIR base without production behind it: the same paths, with /sandbox in front, answered with synthetic data that always gives the same answer, so you can build and test against it. It takes a test key, haau3_sk_test_…, which you make on the API keys page by choosing Test: a test key works only here, and a live key does not work here (see test and live keys). Its first service is the CARIN Consumer Real-Time Pharmacy Benefit Check (STU2 ballot, 2.0.0-ballot), answered by Example Health Plan, a test insurer, as a payer would, so a client written for a payer runs against it with only its base URL changed. It answers for a small cast of test members, and each medication in its formulary table always gets the same answer, so every outcome can be rehearsed on demand: a price with a copay, an alternative drug, not covered, prior authorization, a quantity limit and two errors.
Send test members only, never a real person. Example Health Plan answers only for the test members below, whose details are made up. A request for anyone else is answered with the error no-test-member and nothing more. Nothing you send is stored, cached or logged. See Data handling.
Every organization can call it on every plan. Each request counts toward your plan like any other request, and your Usage page shows it as Sandbox. The open-source SDKs for TypeScript, Python and Go call every operation of this service, and each operation in the API reference has a sample in all three and in cURL.

Service base

The platform’s own FHIR base is https://api.haau3.com/v1/fhir/r4; the Sandbox’s is the same with /sandbox in front. It is a FHIR R4 server with its own CapabilityStatement, which says what the Sandbox serves. It needs a test key (see Authentication), and a live key gets 403 live-key-in-sandbox. It answers application/fhir+json; fhirVersion=4.0, and every error on it is a FHIR OperationOutcome, the 401 without a key included.

What is served

metadata returns the Sandbox’s CapabilityStatement, which names CARIN’s responder statement in instantiates and the member’s request (rtpbc-request) as the one message it receives. $process-message takes a member’s request and answers it in the same exchange, at the same path as the platform’s Pharmacy Benefits. It accepts POST only, with Content-Type: application/fhir+json, and takes no parameters.

Test members

Send a test member’s member ID in any of the places CARIN carries one:
  • Coverage.subscriberId;
  • the Coverage’s rxid class, the member ID on a pharmacy benefit card (http://terminology.hl7.org/CodeSystem/coverage-class);
  • a Patient.identifier whose system is https://example-health-plan.test/member-id.
One of them is enough. Names and birth dates are not compared, so a typo never blocks a test. Two places that name two different test members name no one. Example Health Plan’s Payer ID is SANDBOX-EHP, and the examples name the payer by it, in a Coverage.payor.identifier whose type is payerid from http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType, as the platform’s Pharmacy Benefits finds a member’s insurer. Example Health Plan is the Sandbox’s only test insurer, so it answers every member’s request, whatever payer it names.

Formulary table

A medication is matched by its RxNorm code or its NDC, in the Claim’s item.productOrService.coding. The codes and displays are from RxNorm; the two drugs of CARIN’s own examples, Prozac and Zepatier, keep CARIN’s amounts. The amounts are fixed whatever the quantity you ask about.

What the test data does not cover

Example Health Plan lets you rehearse the shape of a member’s answer and each outcome above. It is not a model of a real plan, and it does not have:
  • a deductible, coinsurance tiers, or a running total of what a member has paid this year;
  • step therapy, or any restriction beyond the ones in the table;
  • prices that differ by pharmacy, network or mail order: the pharmacy you name changes nothing;
  • amounts that follow the quantity or the days’ supply;
  • an answer to the request without patient details, which price sources give;
  • a member’s own sign-in: you send your platform API key, where a payer takes the member’s access token.
It enforces CARIN as published and nothing more, so a production payer that requires more may refuse a request this test insurer accepts.

Send a request

The member’s request is the same message the Pharmacy Benefits page documents: a Bundle of type message whose first entry is a MessageHeader with the event rtpbc-request, then the Claim, the member’s Patient and Coverage, the prescription (MedicationRequest), its prescriber and the pharmacy. That page’s member example answers here as it is, since its member is Mei Lin.
The request, request.json:
request.json
The answer:
answer.json
To try every outcome, change the member ID, or the medication’s coding in both the Claim and the MedicationRequest, to another row of the tables above.

Reading the answer

The answer is a Bundle of type message: a MessageHeader answering yours, one ClaimResponse, and your Patient as you sent it, with a test member’s labelled HTEST (http://terminology.hl7.org/CodeSystem/v3-ActReason) so a copy you keep stays marked as test data. The ClaimResponse names the insurer by its Payer ID, SANDBOX-EHP, and carries either an answer in item or an error in error, never both.
  • item is the medication you asked about. Its restriction is the rtpbc-benefitRestriction extension (covered, prior-auth, quantity-limit or not-covered), and what the member pays is in adjudication, each amount coded by CARIN’s pay type (copay, coinsurance, eligible or total).
  • addItem is an alternative drug, where the row has one: marked rtpbc-isAlternative, with its own restrictions and amounts and the quantity you asked about.
  • processNote always starts with one note saying the answer is synthetic, which test member it is for and which row answered. A limit’s words, such as Quantity limit: 9 tablets per 30 days, are a further note that the item points to with noteNumber.
  • error carries one code from https://haau3.com/fhir/CodeSystem/sandbox-error: no-test-member (no test member matched) or insurer-declined (the insurer declined the request), with outcome error.

What is refused

A request is read by the same rules as Pharmacy Benefits’ member’s request, so a request this base accepts, the platform’s FHIR base accepts too. A request it refuses is answered 400 with an OperationOutcome that names the element by its FHIRPath, never a value you sent: a body that is not a message Bundle, a list over its cap, an element the CARIN profiles require that is missing, a reference that names no entry in the Bundle, a product with no NDC or RxNorm code, or a request with no Coverage or no payer identifier. The request without patient details (rtpbc-request-non-phi) is refused too, not-supported: Example Health Plan answers a member’s request only.

Errors

The statuses every service shares, among them 401, 405, 406, 415, 429 and 500, are on Errors, limits and request ids.

Data handling

Example Health Plan answers with synthetic data, for its test members only, and is not an operation for patient data. It is stateless all the same: nothing a request carries is stored, cached or logged, apart from a request ID you choose to send. The log records the outcome’s code, never a member or a medication, and every answer of $process-message carries Cache-Control: no-store. See Patient data.

Specification