Integration Workflow
This page shows how the pieces of the Eirly API fit together end-to-end, so you can picture the whole integration before diving into the individual REST endpoints and webhook events.
There are two flows to understand:
- Referral ordering — you place an order, we create and issue the referral(s), and you retrieve the referral PDF.
- Results delivery — the lab returns results over time, we notify you as the referral and results progress, and you pull the canonical result data.
If you order Medicare tests, there is a one-off setup per doctor before either flow applies. See Medicare provider setup.
Both flows follow the same pattern:
Webhooks are notifications, not data. Treat every webhook as a signal that something changed, then call the REST API to fetch the current, canonical state. Webhook payloads are intentionally minimal — the REST response is the source of truth.
Throughout, Integrator is your system and Eirly is our API.
Medicare provider setup
Ordering Medicare tests requires a provider record: a doctor's provider number, the details printed on their referrals, and their signature. You create these over the API and the doctor never needs an account with us.
The part that surprises people: creating the record lets you order, but it does not make results come back. Those are two independent halves.
Step by step:
- Create the provider record.
POST /v1/providerswith the number, name, address and a base64 PNG signature. That practitioner can order Medicare tests immediately after this call. - Request result routing.
POST /v1/providers/{providerNumber}/routing, naming the labs (get their ids fromGET /v1/vendors). This records your attestation that you hold the doctor's written authority, and queues one request per lab. It sends no email and contacts no lab — everything it does is inside our system. - We notify the labs. In batches, by our team, because the labs are
non-technical and one email per doctor gets lost. Each request moves to
PENDING_CONFIRMATION. - The lab confirms. Usually days to weeks later. The request becomes
CONNECTED. - Poll for status.
GET /v1/providers/{providerNumber}and readconnections[].status.
A pathology lab returns a result carrying only the ordering provider number, and routes it to us only once it has confirmed with us that the number belongs to your practice. Until then you can place as many Medicare orders as you like and the results will go wherever they go today. Step 2 is not optional.
Which endpoint, when
| You want to | Call | Notes |
|---|---|---|
| Let a doctor start ordering Medicare tests | POST /v1/providers | Number + signature. Ordering works from this call alone |
| Find out which labs you can ask | GET /v1/vendors | The id values you pass in vendorIds. Read it, don't hardcode |
| Make that doctor's results come back to you | POST /v1/providers/{providerNumber}/routing | Queues the request with us. Emails nobody. Needs a signature on file |
| Check whether a lab has confirmed yet | GET /v1/providers/{providerNumber} | Read connections[].status |
| See everything you hold, and each one's status | GET /v1/providers | Cursor paginated. Includes records created in the portal, not just yours |
| Correct a doctor's name or practice address | PUT /v1/providers/{providerNumber} | Keeps the signature and any routing already in flight |
| Replace a doctor's signature | PUT /v1/providers/{providerNumber} | Send encodedSignature. Omit it to leave the existing one alone |
| Stop a doctor ordering (they left the practice) | DELETE /v1/providers/{providerNumber} | Refused while a lab is still routing. Contact support to wind that down |
A worked onboarding run
Onboarding a set of doctors in one go, which is the common case:
GET /v1/vendors # once, to learn the lab ids
for each doctor:
POST /v1/providers # number, name, address, PNG signature
POST /v1/providers/{providerNumber}/routing # providerNumber + vendorIds + attestation
# later, on a schedule:
GET /v1/providers?count=20 # poll until the statuses reach CONNECTED
Both write calls are safe to repeat. POST /v1/providers with a number your
organisation already holds updates that record rather than failing, so a partial
run can simply be re-run. Re-sending a routing request refreshes the attestation
and never regresses a lab that has already confirmed.
After onboarding
Routing is per provider number and survives ordinary edits, so day-to-day changes need nothing special:
- A doctor's details change.
PUTthe record. Routing is untouched, because the labs confirmed the number, not the address. - A doctor's signature changes.
PUTwith a newencodedSignature. It is used on referrals from that moment. - A doctor joins later. Same two calls as the initial run. They will be in
our next batch to the labs rather than the one that already went out, so their
CONNECTEDwill arrive later than their colleagues'. - A doctor's provider number changes. Retire the old record and create the new number. The number is the record's identity and cannot be edited, because any routing already confirmed is bound to it.
- A doctor leaves.
DELETEthe record. If a lab is still routing for them, the call is refused withPROVIDER_HAS_ACTIVE_ROUTING: the lab has to be told to stop first, or it keeps sending us results for someone you no longer represent. Contact support to wind it down.
What these endpoints do not do
- They do not email anyone. No call in this API contacts a lab. Our team does that, in batches.
- They do not create accounts. The doctor never receives an invitation and never signs in. If you also want them using the portal, invite them there separately and ask support to link the two records.
- They do not make routing instant.
CONNECTEDdepends on a third party replying to us, which takes days to weeks. Build for the wait rather than polling tightly.
Full field-level detail in the Providers reference.
Referral ordering
You place an order over HTTP and receive an immediate confirmation. Once the referral has been issued (its PDF generated), we fire a webhook — that's your cue to fetch the referral and download the PDF.
Step by step:
- Place the order.
POST /v1/referralswith the patient and test details. This call can take up to ~10 seconds — allow for it before retrying so you don't create duplicate orders. - Read the synchronous response. The referral(s) come back immediately,
typically with status
CREATED. A single request can create more than one referral (for example a split bundle), so always handle an array. - Wait for
referral.issued. When the referral PDF has been generated, we send thereferral.issuedwebhook. (referral.createdalso fires — see edge cases for the branches you may see instead.) - Fetch the issued referral.
GET /v1/referrals/{uuid}returns the current payload includingreferralAttachment— a time-limited signed URL to the referral PDF. - Deliver the PDF. Download it and/or email it to the patient so they can attend collection. Signed URLs expire, so re-fetch the referral if you need a fresh link.
Ordering edge cases
Not every order goes straight to ISSUED. Depending on the order, you may
receive one of these instead of (or before) referral.issued:
| Webhook / status | What it means | What to do |
|---|---|---|
referral.pending_verification | The profile needs completing before the referral can be issued. | Complete verification; referral.issued follows. |
referral.billing_failed | Billing for the API order could not be completed. | Fix the billing method and re-order. |
referral.scheduled_reissue | A new PDF is being regenerated (e.g. after a PATCH). | Wait for the next referral.issued, then re-fetch. |
See Referral statuses for the full lifecycle.
Results delivery
After collection, the lab returns results — sometimes partially, then in full. Each change fires a webhook. As always, the webhook is the signal; you then pull the full result from the REST API.
Step by step:
- Referral state changes. As results arrive, the referral transitions to
PARTIAL_RESULTSand thenCOMPLETE, firingreferral.partial_results(which can fire multiple times) and finallyreferral.complete. - Result records change. In parallel, the result itself fires
result.createdandresult.updated(andresult.partial_results/result.complete) as biomarker data is populated and finalised. - Fetch the result. Use the
resultsUuidfrom the referral (or the result UUID from the webhook) to callGET /v1/results/{uuid}. - Ingest the canonical data. Each response returns the full result set —
all biomarkers known so far, plus a
biomarkerSummaryand aresultAttachmentPDF URL — not just what changed. You can safely overwrite your stored copy with each fetch rather than trying to merge deltas.
Because every fetch is the full snapshot, handling PARTIAL_RESULTS is simply a
matter of re-fetching on each webhook. When COMPLETE arrives, the same endpoint
returns the finalised set. Some organisations also review results before release
(result.in_review) — those are held until reviewed.
Results you release yourself
If your organisation receives results directly from laboratories, you triage
them in your incoming results and release each one to a client. A released
result fires the same result.* webhooks and is fetched the same way, with
GET /v1/results/{uuid}. It has no referral, so there is no referral.*
webhook for it, and its provenance is INCOMING where an ordered result's is
ORDERED. See
Results not ordered through us.
Putting it together
Testing the full flow in staging
You don't need a real lab to exercise the results half of this flow. In
staging only, PUT /v1/referrals/{uuid} generates dummy results for a
referral, firing the same result webhooks your production integration will see.
Add ?partial to generate a partial set first so you can test the
PARTIAL_RESULTS → COMPLETE transition. See
Simulate Results.
Next steps
- REST API — full endpoint reference for referrals and results
- Webhooks — event list, payloads, signatures, and retries
- Authentication — obtaining an access token