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:
Throughout, Integrator is your system and Eirly is our API.
Medicare provider setup
Section titled “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.
sequenceDiagram
autonumber
participant I as Integrator
participant E as Eirly
participant L as Pathology lab
I->>E: POST /v1/providers
Note over E: Provider record created.<br/>Ordering works from here.
E-->>I: 200 — provider record
I->>E: POST /v1/providers/{providerNumber}/routing
Note over E: Attestation recorded.<br/>One request per lab, NOT_SUBMITTED.<br/>No email is sent by this call.
E-->>I: 200 — connections (NOT_SUBMITTED)
Note over E,L: Our team notifies the labs in batches
E->>L: Routing request
Note over E: PENDING_CONFIRMATION
L-->>E: Confirms the number belongs to your practice
Note over E: CONNECTED — results now route to you
I->>E: GET /v1/providers/{providerNumber}
E-->>I: connections[].status
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.
Which endpoint, when
Section titled “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
Section titled “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 CONNECTEDBoth 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
Section titled “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
Section titled “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
Section titled “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.
sequenceDiagram
autonumber
participant I as Integrator
participant E as Eirly
I->>E: POST /v1/referrals
Note over E: Order received.<br/>Referral(s) created in system.
E-->>I: 200 — referral(s) returned (status CREATED)
Note over E: Referral(s) issued<br/>(PDF generated)
E--)I: Webhook: referral.issued
I->>E: GET /v1/referrals/{uuid}
E-->>I: Referral payload incl. referralAttachment (PDF URL)
Note over I: Download PDF and/or<br/>email it to the patient
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
Section titled “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
Section titled “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.
sequenceDiagram
autonumber
participant I as Integrator
participant E as Eirly
Note over E: Lab returns results over time
E--)I: Webhook: referral.partial_results / referral.complete
E--)I: Webhook: result.created / result.updated
I->>E: GET /v1/results/{uuid}
E-->>I: Full result data (biomarkers, resultAttachment)
Note right of E: The response is the FULL result set,<br/>not a delta — treat it as the<br/>canonical source of truth.
Note over I: Ingest result data, take action
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.
Results you release yourself
Section titled “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
Section titled “Putting it together”flowchart TD
A["POST /v1/referrals"] --> B["Referral CREATED"]
B --> C(["webhook: referral.issued"])
C --> D["GET /v1/referrals/uuid<br/>→ download PDF"]
D --> E["Patient attends collection"]
E --> F(["webhook: referral.partial_results<br/>/ result.updated"])
F --> G["GET /v1/results/uuid<br/>→ ingest full result"]
G --> H(["webhook: referral.complete<br/>/ result.complete"])
H --> I["GET /v1/results/uuid<br/>→ final canonical result"]
classDef hook fill:#f5c6e6,stroke:#c2185b,color:#000;
class C,F,H hook;
Testing the full flow in staging
Section titled “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
Section titled “Next steps”- REST API — full endpoint reference for referrals and results
- Webhooks — event list, payloads, signatures, and retries
- Authentication — obtaining an access token