api-docs/README.md for what earns an entry and what doesn’t.
Tags: API (a resource, scope, or endpoint) · Sandbox (test keys and synthetic data) · Portal (the developer dashboard at developer.clinikehr.com) · Webhooks (endpoints, deliveries and the change feed) · Billing (workspace plans and usage).
Items you send can now be accepted automatically when a clinic chooses
A clinic’s owner or manager can choose, for one key or one connected organization and for each kind separately, to accept your new allergies, conditions, reported medicines and referrals without review. It is off until the clinic turns it on, and you cannot request or detect it. Every field below is new and optional to read. See Automatic acceptance.- The answer to a send can already be decided. It is still
202 Acceptedwith aLocation, but the submission may showreview_status: "confirmed",result_record_idand a newaccepted_automatically: true(falseotherwise). Read the body, not the status code. - The record says how it got there. Allergies, conditions, reported medicines and referrals carry
accepted_by(automatic,clinic, ornullwhile not yet accepted), andreviewed_atandreviewed_by_staff_id, which staynulluntil a clinician marks an automatically accepted item reviewed. - Not presented as checked. An item accepted automatically reads
verification_status: "unconfirmed"(andunconfirmedin FHIR, with anaccepted_byextension) until a clinician reviews it. Updates, retractions and “no known allergies” are never accepted automatically, and an item accepted automatically can no longer be changed or withdrawn (409 submission_not_pending). - It pauses on unusual volume, after which your items wait for review again, and a rolled key or a new connection starts with it off.
- Removed:
409 allergy_exists. Sending an allergy the patient already has is no longer refused. It is accepted into review (202) and a clinician can decline it, and a duplicate is never added to the chart or accepted automatically. If your code handledallergy_exists, that branch will no longer run. See Allergies.
Encounters: read a patient’s visits, and open a draft visit for a clinician to complete
Show a patient’s visits in your own app, and open a visit from a scheduling or intake system. A visit is the clinic’s visit note, so an encounter and its note share oneid. See Encounters.- Read a patient’s visits:
GET /v1/clinics/{clinic_id}/encountersandGET /v1/clinics/{clinic_id}/encounters/{encounter_id}, permissionencounters:read. Each carriesstatus(draftorcompleted),class,period,reasonand the clinician, and never any clinical text or billing. Notes the clinic marked sensitive are not listed. Also available as FHIREncounter. - Open a draft visit with
POST, permissionencounters:write. The answer is201 CreatedandIdempotency-Keyis required. The visit appears in the clinic’s consultation list, marked From your app and not yet reviewed, for a clinician to complete. Nothing is ordered, prescribed, billed, signed or finalised, and there is no field for note text. - Change the type, start, end or reason of a draft your app opened with
PATCH, until a person touches it. Once a clinician edits the draft, including its visit details, or signs it, the change is refused with409 note_not_editable. See Errors. - Permissions:
encounters:readandencounters:writecan now be given to a key, and the Visits kind can be requested in an organization’s application. See Permissions. - Clinicians can now edit a visit’s details on any note, and a visit with no type shows as unknown in FHIR rather than as a guessed type.
- A note’s text still comes from the Notes routes: notes created through the API cannot carry treatment, service or diagnosis blocks.
Referrals: read the referrals a clinic has received, and send one for review
Connect a referring practice’s system to the clinic it refers to. A referral you send is reviewed by the clinic before it joins its referral list, and the clinic alone decides to accept, decline or book it. See Referrals.- Read a patient’s referrals:
GET /v1/clinics/{clinic_id}/referralsandGET /v1/clinics/{clinic_id}/referrals/{referral_id}, permissionreferrals:read. Each carries the clinic’s workflowstatus(received,accepted,scheduled,completed,declined,cancelled,entered_in_error). Also available as FHIRServiceRequest. - Send a referral with
POST, permissionreferrals:write. The answer is202 Acceptedwith aLocationheader, andIdempotency-Keyis required. Follow it withGET /v1/clinics/{clinic_id}/submissions/{submission_id}. - Change or take back a referral you sent with
PATCH, only while the clinic has not acted on it. After the clinic accepts, declines or books it, a change is refused with400 invalid_request. No request can accept, decline, book, complete or cancel a referral. - ICD-10 reason codes are checked against a verified list, and one that is not a real ICD-10 code is refused.
- A reason stays private. The reason the clinic gives for declining or cancelling is shown only to the key or organization that sent the referral.
- Permissions:
referrals:readandreferrals:writecan now be given to a key, and the Referrals kind can be requested in an organization’s application. See Permissions.
Providers and outside care providers: read the clinic’s clinicians, and keep a patient’s outside contacts
Show who a patient will see, and keep the doctors, referrers and specialists a patient names up to date. See Providers and Outside care providers.- Read the clinic’s clinicians:
GET /v1/clinics/{clinic_id}/providersandGET /v1/clinics/{clinic_id}/providers/{provider_id}, permissionproviders:read. Each carries name, role, specialties, NPI and licences. It is read-only, holds no contact details and no verified flag, and is not a patient-data permission. Also available as FHIRPractitioner. - Read a patient’s outside care providers:
GET /v1/clinics/{clinic_id}/care-providersandGET /v1/clinics/{clinic_id}/care-providers/{care_provider_id}, permissioncare_providers:read. Every contact is listed withsourceand a neweditableflag. - Add one with
POST, permissioncare_providers:write. The answer is201 CreatedandIdempotency-Keyis required. It is saved straight away and appears at the clinic marked From your system. - Change or remove only what your app added.
PATCHand the newDELETE(204 No Content) work on contacts your app added. On a contact the clinic or another app added they are refused with409 care_provider_clinic_managed. See Errors. - Permissions:
providers:read,care_providers:readandcare_providers:writecan now be given to a key, and the Outside care team kind can be requested in an organization’s application. See Permissions. - The Patients routes keep showing
care_providersas read-only; use the new routes to add one.
Medications: read reported medicines and prescriptions, and send a reported medicine for review
Connect an intake or records system to the medicines a patient reports taking, and read the prescriptions written for them. A reported medicine you send is reviewed by a clinician before it reaches the chart. See Medications.- Read a patient’s reported medicines:
GET /v1/clinics/{clinic_id}/medication-statementsandGET /v1/clinics/{clinic_id}/medication-statements/{medication_statement_id}, permissionmedications:read. Also available as FHIRMedicationStatement. - Send a reported medicine, or ask for a change, with
POSTandPATCH, permissionmedications:write. The answer is202 Acceptedwith aLocationheader, and nothing reaches the chart until a clinician accepts it. Follow it withGET /v1/clinics/{clinic_id}/submissions/{submission_id}. - Read a patient’s prescriptions:
GET /v1/clinics/{clinic_id}/prescriptionsandGET /v1/clinics/{clinic_id}/prescriptions/{prescription_id}, permissionmedications:read. They are read-only and carry only the drug, dose, frequency, status and dates; prescriptions sent electronically are included withsource: "eprescribe". Also available as FHIRMedicationRequest. No request can create or change a prescription. - RxNorm codes on a reported medicine are accepted only when they are on our verified list. A code we do not recognise is refused; send the medicine as text, or with a
localcode. - Pharmacy checks use it. A confirmed reported medicine with a recognised code is included in the clinic’s drug interaction checks while it is active. One without a recognised code is shown to the pharmacist as not checked, and is never matched by its name.
- ICD-10 codes are now validated. A condition sent with an
icd-10-cmoricd-10code that is not a real ICD-10 code is refused withinvalid_request. Before, only the shape of the code was checked. - Permissions:
medications:readandmedications:writecan now be given to a key, and the Medications kind can be requested in an organization’s application. See Permissions. - The Patients routes keep refusing
current_medications; send reported medicines here instead.
Allergies: read a patient’s allergies and send one for review
Connect an intake or records system to a patient’s allergies and intolerances. An allergy you send is reviewed by a clinician before it reaches the chart. See Allergies.- Read a patient’s allergies:
GET /v1/clinics/{clinic_id}/allergiesandGET /v1/clinics/{clinic_id}/allergies/{allergy_id}, permissionallergies:read. Also available as FHIRAllergyIntolerance. - Send a new allergy, or ask for a change, with
POSTandPATCH, permissionallergies:write. The answer is202 Acceptedwith aLocationheader, and nothing reaches the chart until a clinician accepts it. Follow it withGET /v1/clinics/{clinic_id}/submissions/{submission_id}, which now works for the kind of item you sent. - Names on the clinic’s plain allergy list are returned too, as read-only items with
entry: "legacy_text"and noid, so a patient is never shown as having no allergies when the chart says otherwise. An empty list does not mean “no known allergies”. - RxNorm codes are accepted only when they are on our verified list. A substance such as “none” or “NKDA” is refused.
- Permissions:
allergies:readandallergies:writecan now be given to a key, and the Allergies kind can be requested in an organization’s application. See Permissions. - “No known allergies” can be sent as a statement. It is reviewed like an allergy, and a clinician cannot confirm it while the patient has an active allergy on the chart.
- Breaking change, Patients routes: creating, updating or importing a patient no longer accepts
allergies,current_medicationsorchronic_conditions. A request that includes any of them is refused. These fields used to write the patient’s record directly, with no review. Send allergies with Allergies and conditions with Conditions instead; both are reviewed by a clinician before they reach the chart. Current medications cannot be set through the API. - New error code
allergy_exists(409): the patient already has an active, confirmed allergy to that substance. - FHIR
Conditionfix: the extra ClinikEHR fields on a FHIRCondition(review_status,source,origin_assistant_name,submission_id,decision_reason) are now valid FHIR extensions, each carrying avalueString. Before, they carried a barevalue, which FHIR validators reject. - Blank notes created through the API no longer accept order or billing content (
diagnosisData,treatment_plans,follow_up_plans,services,diagnoses) or underscore-prefixed fields; such a request is refused withunknown_field.
Insurance: list a clinic’s payers, read and add a patient’s coverage
Connect an intake or eligibility system to a patient’s insurance. A coverage you add stays inactive until the clinic’s staff confirm it. See Insurance.- Payers:
GET /v1/clinics/{clinic_id}/payersandGET /v1/clinics/{clinic_id}/payers/{payer_id}, permissionpayers:read. Only the payers the clinic has enabled under Settings → Insurance are listed. - Coverage: read with
GET /v1/clinics/{clinic_id}/coveragesand…/coverages/{coverage_id}(coverage:read). Add withPOST(201,Idempotency-Keyrequired) and change withPATCH(coverage:write). - Confirm before active: a coverage you add is
unconfirmedand inactive. Staff confirm it at the clinic, and only then can it be used for eligibility checks and claims. Changing a confirmed coverage’s payer, member ID or subscriber returns it tounconfirmed. - FHIR: read a coverage as
Coverageand a payer asOrganizationwithAccept: application/fhir+json. - Permissions:
payers:read,coverage:readandcoverage:writecan now be given to a key, and the Insurance coverage kind can be requested in an organization’s application. See Permissions. - Three new error codes, all
409:coverage_exists,payer_not_enabledandactivation_requires_staff.
Conditions: read a patient’s problem list and send a diagnosis for review
The first clinical resource that goes through clinician review. See Conditions.- Read a patient’s problem list:
GET /v1/clinics/{clinic_id}/conditionsandGET /v1/clinics/{clinic_id}/conditions/{condition_id}, with permissionconditions:read. Also available as FHIRCondition. - Send a new condition, or ask for a change, with
POSTandPATCH, permissionconditions:write. The answer is202 Acceptedwith aLocationheader. Nothing reaches the chart until a clinician accepts it. Marking a conditionentered_in_erroris a retraction, reviewed the same way. - Follow it with
GET /v1/clinics/{clinic_id}/submissions/{submission_id}, or list your own withreview_status. - Permissions:
conditions:readandconditions:writecan now be given to a key, and the Conditions kind can be requested in an organization’s application. See Permissions. - Two new error codes:
client_reference_conflictandsubmission_not_pending, both409.
The FHIR guide now matches the API, and there is a page for SDKs and Postman
The FHIR guide had drifted from what the API does. It is corrected, and two new pages are added.- FHIR R4 no longer says FHIR is not deployed. It now says updates are
PATCH(neverPUT), that a write is answered in our plain JSON shape, and lists the search parameters each resource accepts. - SDKs and Postman says what exists, that the TypeScript and Python libraries are not published yet, and how to use the Postman collection against
https://api.clinikehr.com/v1. - Reviewing outside submissions explains pending, confirmed and rejected, and
202 Accepted. No endpoint uses it yet. - The overview no longer says CRM has no route. CRM contacts and activities are patient-data permissions like the others.
Workspace settings have their own sidebar, and your account has its own page
The Settings entry in the dashboard no longer opens a drop-down. Workspace settings now sit in a sidebar of their own (a menu at the top of the page on a phone or tablet), and each page is shown only to the roles that can use it.- Profile and Settings in the header now opens your own account page — your name, password, two-factor authentication and passkeys, and email preferences. It previously led to a page that did not exist.
- See Workspace settings and your account.
- The API keys list now fits a phone and pages once you have more than ten keys. Each key shows its first two permissions with a +N for the rest, and selecting a key opens a panel with its full permissions, dates, allowed addresses and status. See Authentication.
- Creating a key and adding a webhook endpoint each have a Select all box above their lists, and a key’s ID says plainly that it is not the key.
- Documentation and API Reference buttons now sit in the header, just before the Test / Live switch, and open this site in a new tab (on a phone they are in the menu).
- The Overview request card now counts requests made today, and shows how many succeeded, were refused (4xx) or failed (5xx), with a per-day chart. Before, requests made today were left out, so a new workspace could read “No requests in this environment yet.”
- The Test / Live switch is now clear on every page. A strip under the header always says which mode you are in (red on Live), the choice is remembered when you reload or follow a link without it, and API keys, Request logs, Webhooks and Overview each say which mode they show. Webhooks now list only the endpoints for the mode you are in. Members, Plan, Settings and the other workspace pages are the same in both modes.
- Agreements now shows the full API Terms of Use, with the Acceptable Use Policy, and, for an organization applying for patient data, the Data Protection Addendum for Organizations. An Admin or Owner accepts the terms for the workspace. A live key cannot be created until they are accepted; a workspace that already had live keys has 30 days from publication before they pause. See Workspace settings and your account.
Try every resource in your sandbox, with a request to paste for each
The Sandbox page in your workspace now says exactly what is in your sandbox and gives you a ready-to-paste request for each resource. A test key can now hold the patient-data permissions, because it reaches only your workspace’s sandbox — invented patients, notes and appointments, never a real clinic.- Patients, notes and appointments: six invented patients (named “SANDBOX”), four invented notes, and invented services and clinicians with weekly hours. Create, read, change and cancel patients and notes; book and cancel appointments (to move one, cancel it and book again). Nothing is sent to anyone.
- Network search: searching by name or barcode now returns results from the sample pharmacies, and feedback on a result is accepted.
- Your own medicine codes: four sample codes, one in each state; list, read, upload and delete them.
- Reset sandbox puts all of it back to the starting data.
- Webhooks is the new name of the Notifications page; the old address redirects.
- Referred pharmacies now shows an explanation instead of an error when you have not referred anyone yet.
Manage webhook endpoints and your own medicine codes from the dashboard
The Webhooks (previously called Notifications — the old address redirects) and Partner codes pages in your workspace now do what they describe. Both need the Admin role or higher in the workspace.- Webhooks: add an endpoint, send a test event, read the delivery log, replay a delivery that failed, rotate the signing secret, and delete an endpoint. The signing secret is shown once, when you create or rotate it — copy it then. See Verifying a webhook’s signature.
- Partner codes: upload a CSV of your own medicine codes, see which matched automatically, which are waiting for a pharmacist’s confirmation and which did not match, and delete a code you no longer want. See Uploading your own medicine codes.
- The Connect to a clinic sheet now says plainly that patient information can’t be requested yet: an organization can’t receive patient data today, on any plan.
Find and check a pharmacy across the network, before you have a direct connection
Two new routes let your organization discover a pharmacy and check what it has, without connecting to it first:GET /v1/listings/pharmacies lists pharmacies that have listed themselves to your organization, and POST /v1/listings/search searches by barcode, NAFDAC number, or name across all of them at once.- Requires
listing.profile:read(for the list) orlisting.availability:read(for a search) — new scopes, granted the same way as any other connection scope. - A search result’s availability can come back
unknownwhen a pharmacy’s own point-of-sale hasn’t synced recently — see Stock bands for what that means and why it’s never shown as out of stock. - Once you hold a connection to a specific pharmacy, its full listing is also readable directly:
GET /v1/clinics/{clinic_id}/listing/profileand.../listing/items.
See your workspace’s plan and this month’s usage before you go live
Every developer portal workspace now has a plan — Sandbox to start, Essential or Enterprise once you’re ready for a live key — with a dashboard page showing exactly where you stand.- Plan & usage, inside your workspace, shows this month’s requests and connected clinics against what’s included, and this month’s computed overage charge so far, in USD and NGN.
- Going over Essential’s included requests or connected clinics is billed automatically — a live key is never interrupted mid-month for it. See Usage and billing.
- A live key on a workspace still on the Sandbox plan is refused with
code: "workspace_plan_required"until the workspace is on a paid plan — see Plans and pricing and Errors.
Approve, narrow, or revoke exactly what another organization can reach
A clinic can now approve a connection from an outside organization item by item, instead of an all-or-nothing grant — and see, at any time, exactly what it has shared.GET /v1/connectionslists every clinic (or, for a test key, your sandbox) a key currently reaches, with the scopes actually granted for each one.- A clinic narrows or revokes what’s granted at any time — a revoked connection stops answering on your very next call, nothing to catch or retry around.
- See Connections.
Build against a sandbox before you ever touch a real clinic
Signing up for a developer portal workspace now gets you anehr_test_ key immediately — no verification needed — that reaches a fixed, fictional clinic seeded with synthetic inventory. The same router, the same validation, the same error codes as a live key; only the data behind it is fake.- A test key can never reach a real clinic, and a live key can never reach the sandbox — each direction is refused, not just discouraged.
- Moving to a live key needs your workspace verified (a domain check plus a completed profile) and on a paid plan — see Authentication.
- See the two ways to use this API for how a clinic’s own key and an organization’s key differ.
Read a clinic’s own inventory straight from your own systems
A clinic can create a key, under its own Settings → API access, that reads straight out of its ClinikEHR workspace — no separate account, no separate documentation to reconcile.- Three read scopes:
inventory.availability:read(a band, not a number),inventory.items:read(catalogue detail), andinventory.stock:read(exact quantities, the most sensitive of the three). - Every list is cursor-paginated and accepts
updated_since, so a periodic sync only pulls what changed — see Pagination. - Every failure is a consistent
application/problem+jsonbody with a stablecodeyou can branch on — see Errors. - See Inventory and Getting a key.