How to Connect an HMO Portal to a Licensed Pharmacy in Nigeria: Stock Availability and Drug Request API (Developer Guide, 2027)
A developer guide to the ClinikEHR API: read a licensed pharmacy's stock bands, upload your own medicine codes and send drug requests to dispensing.
By ClinikEHR Team
Duration
27 MINSYou are building a portal for an HMO, a health maintenance organization that pays for its members' care. You want it to show whether a licensed pharmacy has a medicine, and to ask the pharmacy to prepare it. This guide shows how to connect an HMO portal to a licensed pharmacy in Nigeria with the ClinikEHR API (api.clinikehr.com), the API of the ClinikEHR app. ClinikAPI (clinikapi.com) is a different product, built on the FHIR R4 health data standard, with its own site, keys and licence.
This guide assumes you hold an approved organization's key. Your HMO or health-tech team is one organization. The pharmacy, which runs the ClinikEHR Pharmacy edition, is another.
Quick Answer
To connect an HMO portal to a licensed pharmacy, get an organization key, let the pharmacy connect you, read availability, upload your own medicine codes, then send drug requests.
- Get access: a verified developer workspace and a key. Drug requests and dispensing history also need patient-data approval, and the pharmacy must be on the Enterprise plan.
- Connect: the pharmacy's owner hands you a one-time connection code and approves your scopes.
- Read: list the locations if you need them, then ask for availability bands (out, low or in_stock) with an
as_oftime. A band is never a reservation. - Map: upload your own medicine codes in a body with the key
entries. Only exact matches link. Name-only matches wait for review. - Request: send a drug request with an Idempotency-Key, poll its status, then read the dispensing history.
What Stock Availability and a Drug Request Are, in Plain Words
An API is a set of requests one program sends to another. The ClinikEHR API lets your portal read data from a pharmacy's ClinikEHR account and send it requests. Each request carries a key, a secret string that proves who you are. A key can only do what its scopes allow. A scope is a named permission, such as inventory.availability:read. A workspace is your team's own account in the developer portal, separate from any pharmacy's data.
A stock availability band is one of three words: out, low or in_stock. A drug request is how your system asks the pharmacy to prepare medicines for one customer linked to your HMO. The pharmacist decides what happens next. The API takes drug requests. It does not take prescriptions or e-prescriptions.
A sandbox is a practice area of invented data. Idempotency means that sending the same write twice has the effect of sending it once.
The Whole Flow at a Glance
The journey below is illustrative. The pharmacy, the medicine and the customer are invented. Each step is explained later.
| Illustrative step | Route | Scope |
|---|---|---|
| The portal lists the pharmacy's locations, if it needs them | GET /v1/clinics/YOUR_CLINIC_ID/inventory/locations | inventory.stock:read |
| It asks whether a maintenance medicine is in stock and gets low | POST /v1/clinics/YOUR_CLINIC_ID/inventory/availability | inventory.availability:read |
| It uploads the HMO's own medicine codes | POST /v1/clinics/YOUR_CLINIC_ID/inventory/codes | inventory.items:write |
| It creates a drug request for a customer linked to the HMO | POST /v1/clinics/YOUR_CLINIC_ID/drug_requests | requests:write |
| It reads the request until it shows ready | GET /v1/clinics/YOUR_CLINIC_ID/drug_requests/REQUEST_ID | requests:read |
| It reads the dispensing history to see what was handed over | GET /v1/clinics/YOUR_CLINIC_ID/dispensing/history?patient_id=PATIENT_ID | dispensing:read |
Before You Start: Access for an Organization Key
Your HMO or team is an organization in ClinikEHR's developer portal. You never hold the pharmacy's keys. Your key reaches only pharmacies that approved a connection to your workspace, and each connection carries only the scopes the pharmacy granted.
There are two tiers of access. Stock, locations, availability and your own codes are not patient data. Drug requests and dispensing history are.
| Tier | Routes | What must be in place |
|---|---|---|
| Not patient data | Locations, availability, exact stock, your codes | A verified workspace on a paid plan, a pharmacy plan that includes API access, and a connection that grants the scope. No patient-data approval |
| Patient data | Drug requests, dispensing history | Everything above, plus the approvals below |
A workspace moves to a paid plan by request, and there is no self-serve checkout. The Essential plan covers non-patient data only.
What the patient-data routes add
On your side:
- A verified workspace on the Enterprise contract plan. ClinikEHR sets it on request, so contact ClinikEHR.
- Approval for the kinds of data you need: Patients, Drug requests and Dispensing history. A ClinikEHR reviewer decides, and the documentation states no timeline.
- Acceptance of the current data-protection addendum, version 2026-10-09, between Bettar Platforms, Inc. and your organization. Its Nigeria clause has terms under the Nigeria Data Protection Act (NDPA). This guide is not legal advice.
On the pharmacy's side:
- The pharmacy is on the Enterprise plan.
- Its owner generates a one-time connection code and hands it to you directly.
- The pharmacy grants the scopes to your connection, and a legal basis for sharing patient data is recorded on it.
- The customer is linked to your connection.
The pharmacy can narrow your access or end the connection at any time, and access stops with the next request. This describes how access works. It does not say that any HMO has been approved or connected.
Connect and Read Stock Availability
Step 1: Open a workspace and create a test key
Sign up in the developer portal, create a workspace, then create a test key. A test key starts with ehr_test_ and reaches only a sandbox of invented data. A live key starts with ehr_live_ and reaches real data. Its end date, chosen when you create it, is at most 12 months away.
Every request carries one bearer key in an Authorization header that reads Bearer, then the key. Call the API from your own backend.
What you need: a developer workspace. A test key needs no verification.
How you know it worked: your first inventory read with the test key answers with invented data.
The mistake people make: calling the API from the browser, where anyone can read the key.
Step 2: Get the pharmacy's connection code
You find a pharmacy only through a one-time connection code that its owner generates and hands you. The connections page explains what happens next. The pharmacy then approves the scopes for your connection. Ask for the fewest that do the job. A stock badge needs inventory.availability:read and almost never needs inventory.stock:read. The permissions page lists every scope.
What you need: the code, and a list of the scopes you want.
How you know it worked: a read on that pharmacy answers with data. A missing scope answers 403 with the code scope_missing. A record your key cannot reach answers 404 not_found, the same as one that does not exist.
The mistake people make: treating one connection as access to every pharmacy. Each pharmacy approves its own.
Step 3: List the pharmacy's locations
Locations are the storerooms, shelves or fridges where the pharmacy holds stock. Read them with GET /v1/clinics/YOUR_CLINIC_ID/inventory/locations, where YOUR_CLINIC_ID names the pharmacy. The route needs inventory.stock:read. The scope inventory.items:read is not enough. Locations are optional: an availability check does not use one, so skip this step if your portal does not show where stock is held.
This is the API reference's example response. A real call needs a live key and a connected pharmacy, and the id and name are samples.
{
"data": [
{
"id": "6a9f7e3e-2b5a-4a86-8f0d-4a6a2b3c9a11",
"name": "Main storeroom",
"type": "room",
"is_active": true,
"updated_at": "2026-09-20T10:00:00Z"
}
],
"has_more": false,
"next_cursor": null
}
Lists come back a page at a time. Use limit (default 25, at most 100), and pass each next_cursor as starting_after until has_more is false.
What you need: inventory.stock:read on the connection.
How you know it worked: the answer is a list with has_more and next_cursor.
The mistake people make: asking only for the catalogue scope and meeting a 403.
Step 4: Read availability bands
Availability checks up to 100 items in one request, with POST /v1/clinics/YOUR_CLINIC_ID/inventory/availability. It needs inventory.availability:read. You get a band for each item, never an exact number.
The body has one key, entries. Each entry is either an item_id or a pair of system and value, never both. The systems are gtin (the barcode number), nafdac (the national drug registration number), ndc, rxcui (an RxNorm code) and partner (your own code, from Step 6). This is the API reference's sample body. Its ids are examples, and a real call needs a live key and a connected pharmacy.
{
"entries": [
{ "item_id": "9c1e6f2a-9b2b-4a36-8f6a-2f7b1e8b9a02" },
{ "system": "gtin", "value": "6001234567890" }
]
}
This is the API reference's sample response to that request.
{
"data": [
{
"item_id": "9c1e6f2a-9b2b-4a36-8f6a-2f7b1e8b9a02",
"matched": true,
"band": "in_stock",
"as_of": "2026-09-20T10:00:00Z"
},
{
"item_id": null,
"matched": false,
"band": null,
"as_of": null
}
]
}
| Band | Meaning |
|---|---|
out | The quantity is zero or less |
low | At or below the pharmacy's own reorder threshold |
in_stock | Above that threshold |
The pharmacy sets the thresholds. For a connected pharmacy, a band is always one of these three. Three rules keep your portal honest.
as_ofis the time of the item's last recorded stock movement, or the moment of the read if the item has never moved. It is not a freshness guarantee. Show it next to the band.- A band is not a reservation. Nothing is held, and stock can change between your read and the sale.
matched: falsemeans the identifier did not resolve to an item, and the entry carries no band. It is not the same asout.
Do not treat a cached band as current forever. Refresh it on a timer or on an event. The event inventory.availability.changed is sent when an item's band changes, never for every unit sold. It needs a registered webhook, a web address of yours that ClinikEHR calls when something happens. The events page explains the change feed.
What you need: inventory.availability:read, and an identifier for each medicine.
How you know it worked: each entry answers with a band and an as_of time, or with matched: false.
The mistake people make: showing a band with no time beside it.
Step 5: Read exact stock only when you need it
Exact quantities by location and lot come from GET /v1/clinics/YOUR_CLINIC_ID/inventory/stock. The route needs inventory.stock:read, a more sensitive permission than availability, and the pharmacy decides whether your connection gets it. You can filter by location_id, item_id and expiring_before. This is the documentation's sample response, and a real call needs a live key and a connected pharmacy.
{
"data": [
{
"item_id": "item_01example",
"location_id": "loc_01example",
"lot_id": "lot_01example",
"quantity": "42",
"as_of": "2026-09-27T09:02:11Z"
}
],
"has_more": false,
"next_cursor": null
}
The quantity is a decimal string, so parse it as one. No inventory route returns cost price, vendor details, margins or anything about a customer or a sale.
What you need: inventory.stock:read.
How you know it worked: each row names an item, a location, a lot and a quantity.
The mistake people make: asking for exact stock to build a badge that a band would serve.
Teach the API Your Own Medicine Codes
Step 6: Upload your codes
Your systems may name a medicine with a code that is not a barcode, NAFDAC number, NDC or RxNorm code, such as your own internal SKU, a stock-keeping code. Register that code once, and availability resolves it across the pharmacies your workspace is connected to.
Upload with POST /v1/clinics/YOUR_CLINIC_ID/inventory/codes. It needs inventory.items:write, because there is no separate permission for codes. One request takes up to 1,000 rows in a body with the key entries. Each row needs a code. It may also carry barcode, nafdac_no, ndc, rxcui, name, strength, form and pack_size. The rows below are the documentation's sample. The body key is entries, the key the API accepts. A real upload needs a live key and a connected pharmacy.
{
"entries": [
{ "code": "SKU-10021", "barcode": "6001234567890", "name": "Amoxicillin 500mg capsules" },
{ "code": "SKU-10088", "name": "Paracetamol 500mg tablets" }
]
}
Uploading a code you already sent updates it, so a repeat is never rejected as a duplicate, and an Idempotency-Key is optional here. A row field the schema does not know is refused by name, without stopping the rest of the batch. No other workspace sees your codes. The dashboard's Partner codes page checks and uploads a CSV file. It needs the Admin role or higher.
What you need: inventory.items:write, and a barcode or registration number for each row where you have one.
How you know it worked: the answer lists each code with a state, as in Step 7.
The mistake people make: sending names alone and expecting links.
Step 7: Read the state of each row
Each row ends in one of four states. This is the documentation's sample response.
{
"data": [
{ "code": "SKU-10021", "state": "linked" },
{ "code": "SKU-10088", "state": "needs_review", "reason": "matched by name only β several possible medicines" }
]
}
| State | Meaning |
|---|---|
linked | Matched on an exact barcode, NAFDAC, NDC or RxNorm identifier |
needs_review | Matched by name only, or more than one medicine matched. A person confirms before it is trusted |
unmatched | Nothing matched |
rejected | A reviewer said no. It never changes on its own |
A row is never linked on a name match alone, because the wrong match would be the wrong drug. Once a code is linked, an availability check with system: "partner" resolves it to the pharmacy's item. A code that is not confirmed answers matched: false, never out of stock. An item that the pharmacy has not matched to a medicine cannot be found by a partner code either.
This is a map from your code to the pharmacy's item. It is not a formulary, and it does not say what a plan pays for. The partner codes guide also covers listing and deleting your codes.
What you need: the response from Step 6.
How you know it worked: an availability check on a linked partner code answers with a band.
The mistake people make: reading matched: false on an unconfirmed code as a stock-out.
Send Drug Requests and Follow Them to Dispensing
Step 8: Create a drug request
A drug request is a request, not a guaranteed dispense. The pharmacist always decides. Availability is not a commitment. The drug request is. It carries no price, ever. No money moves through it, and payment and claims stay outside this API.
You need requests:write and the patient-data approvals above. The customer must already be linked, because a request cannot name someone who is not. An organization key sees only the patients its connection reaches. The patients page shows how to create and search them.
A request has these parts.
- The customer, as
clinic_patient_id. - One or more lines. Each line needs a
quantity. It may carry acode_systemandcode_value, adescriptionanddirections. - Optionally
needed_by, your ownrequester_referenceand anauthorization_reference.
The authorization_reference is never checked or resolved. It rides along for your own records. If you leave needed_by out, it defaults to seven days after creation. A request nobody actions by then expires on its own, and nothing is sent.
The create route needs an Idempotency-Key header, a unique value for each request, such as a UUID. A retry with the same key returns the first result and creates no second request. The body below was assembled from the API reference's field list, with placeholder values. It is not a documented example. The key is the documentation's own sample, and the ids, code and reference are invented.
POST /v1/clinics/YOUR_CLINIC_ID/drug_requests
Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7
{
"clinic_patient_id": "9d2e7a10-2222-4b3c-8d4e-123456789abc",
"lines": [
{
"code_system": "gtin",
"code_value": "6001234567890",
"quantity": 30,
"directions": "One tablet each morning"
}
],
"requester_reference": "PORTAL-REQ-0001",
"needed_by": "2026-11-01T09:00:00Z"
}
The documentation shows no sample response for this route, so this guide shows none. Read the request back with GET /v1/clinics/YOUR_CLINIC_ID/drug_requests/REQUEST_ID to see its lines. A line the pharmacy cannot match to its catalogue, or that matches more than one item, shows in the pharmacy's app as Not found in your catalogue. The pharmacist then matches or substitutes it by hand, and the system never guesses. The gtin code above matches only if the pharmacy's item carries that barcode.
What you need: requests:write, a linked customer and an Idempotency-Key.
How you know it worked: the pharmacy's app shows the request, and your read shows its status.
The mistake people make: expecting authorization_reference to do anything. The request never checks or resolves it.
Step 9: Follow the request to its end
A request moves through these statuses.
| Status | What it means |
|---|---|
submitted | Created and waiting for the pharmacist |
accepted | The pharmacist accepted it |
partly_accepted | The pharmacist accepted some lines |
substituted | The pharmacist proposes a different product for one or more lines. You confirm before it proceeds |
declined | The pharmacist declined, with a visible reason. Terminal |
ready | The pharmacist marked it ready |
dispensed | Handed over at the till. Terminal |
cancelled | Withdrawn by either side, with a reason and who cancelled it. Terminal |
expired | Never actioned in time. Terminal |
The pharmacist works in the pharmacy's own app and decides each request there. No route lets your system accept, mark ready or dispense.
Your system can do four things.
- Read one request with its lines, with
GET .../drug_requests/REQUEST_ID. - List your requests, with
GET .../drug_requestsand an optionalstatusfilter. - Cancel with
POST .../drug_requests/REQUEST_ID/canceland an optional reason. A status that does not allow a requester cancel answers 409invalid_status_transition. - Confirm or decline a proposed substitute with
POST .../drug_requests/REQUEST_ID/confirm_substitution. It applies only fromsubstituted. Declining ends the whole request.
The cancel body is optional. The reason text is invented.
{ "reason": "Customer no longer needs it" }
The confirm body takes accept or decline.
{ "decision": "accept" }
Your system sees only the requests its own connection created. Read a request to see its status, and poll the list on your own timer, inside the rate limits. The pharmacist cannot accept, decline or update a request while the pharmacy's connection is down. They see it as it was left when the connection returns.
What you need: requests:read, and the request id.
How you know it worked: the status changes as the pharmacist acts, and ends in dispensed, declined, cancelled or expired.
The mistake people make: polling in a tight loop. Poll the list with a status filter.
Step 10: Read the dispensing history
A request reaches dispensed at the till, and the item then appears in the dispensing history. The history is read-only. For one customer it lists each item dispensed, with the medicine, quantity, days supply and date. It never shows a price, another customer or a staff name. Every read is recorded in the pharmacy's access log.
The route is GET /v1/clinics/YOUR_CLINIC_ID/dispensing/history?patient_id=PATIENT_ID. It needs dispensing:read, the approval for the Dispensing history kind, and a linked customer. A customer who is not linked, one the pharmacy has restricted and one who does not exist all answer the same 404. The documentation's example uses a test key, which reaches a sandbox returning two invented dispensed items for any customer id, and one invented flag. A real call needs a live key and a connected pharmacy.
curl "https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID/dispensing/history?patient_id=PATIENT_ID&limit=25" \
-H "Authorization: Bearer ehr_test_EXAMPLEKEYID0000000000_EXAMPLESECRETβ¦"
{
"data": [
{
"id": "4e8c2a10-1111-4a2b-9c3d-0123456789ab",
"medicine": {
"name": "Example Amoxicillin 500mg",
"generic_name": "amoxicillin",
"brand_name": null,
"identifiers": [{ "system": "nafdac", "value": "A4-0000-EXAMPLE" }]
},
"quantity": 21,
"days_supply": 7,
"dispensed_at": "2026-09-25T10:15:00Z"
}
],
"has_more": false,
"next_cursor": null
}
The history is ordered by id, not by date. Read every page, then sort by dispensed_at. A days_supply of null means the pharmacy did not record it, so read it as unknown, not as zero. A second read-only route returns the flags the pharmacist recorded. The guide on flagging early refills and duplicate dispensing explains them, and the dispensing page has the reference.
What you need: dispensing:read, and a linked customer.
How you know it worked: a request you saw reach dispensed shows up as a line in the history.
The mistake people make: assuming the list is newest first.
Use the Published Clients
Step 11: Install a client and call the routes
Two clients are published. Install only these two package names, and nothing with a similar name.
npm install @clinikehr/api
pip install clinikehr
The TypeScript client, @clinikehr/api, runs on Node.js 18 or later. The current version is 0.1.3, so use 0.1.3 or later. Versions 0.1.0 and 0.1.1 are deprecated. The Python client, clinikehr, needs Python 3.11 or later, and its latest version is 0.1.1. Use its synchronous calls. The Python guide shows how a route name maps to a function.
The TypeScript client throws an error if it runs in a web browser, whatever the key. It retries network failures, 429 and 5xx answers for you. This setup is the documentation's own.
import { createClient } from "@clinikehr/api";
const client = createClient({ apiKey: process.env.CLINIKEHR_API_KEY! });
This is the documented call for a drug request. The body is a placeholder in the documentation. The TypeScript guide has the rest.
await client.POST("/v1/clinics/{clinic_id}/drug_requests", {
params: { path: { clinic_id: clinicId } },
body: { /* the drug request */ },
idempotencyKey: `drug-request:${message.id}`,
});
What you need: Node.js 18 or later, or Python 3.11 or later, and a test key.
How you know it worked: createClient returns a client and a first read answers.
The mistake people make: installing an unofficial package with a similar name.
If You Are the Pharmacy's Own Developer
An HMO never holds the pharmacy's keys. A pharmacy's own developers use a different key, which the pharmacy creates under Settings, API access.
- The key reaches that one pharmacy only. It is always a live key, because a pharmacy's own key has no test mode.
- Stock, locations, availability and codes need a plan with API access, and no add-on or agreement, since no patient data is involved.
- Drug requests and dispensing history are patient data. They need the API add-on (an Enterprise contract includes it) and the owner's acceptance of the current API data agreement. The dispensing page also says the pharmacy's subscription must be Enterprise for that permission.
- There is no connection code. The key reads the pharmacy's own customers.
Check the dispensing page for your plan before you build, or ask through the contact page. The Help Center page on choosing a plan lists the Pharmacy edition plans.
Mistakes That Cost Portal Teams Time
- Not handling a withdrawn connection. Handle a 403 or a 404 in your code, because access can end at any time (see Before You Start).
- Letting a live key lapse. A live key stops at its end date. Its owners are emailed 30 days and 7 days before, so make sure someone reads that email.
- Retrying a 429 at once. A 429 answer carries a
Retry-Afterheader. Wait that long before the next try. - Putting a member's name in
requester_reference. Use an internal reference such as PORTAL-REQ-0001 that you can look up yourself. - Reading one pharmacy's history as the whole picture. It lists what this pharmacy dispensed and says nothing about any other.
Frequently Asked Questions
What is a pharmacy stock availability API for an HMO portal in Nigeria?
It lets a portal ask a licensed pharmacy's ClinikEHR account whether a medicine is out, low or in_stock. The answer is a band with an as_of time, not an exact count and not a reservation. It needs an organization key, a connection the pharmacy approved and the inventory.availability:read scope. It carries no patient data.
Does an availability band reserve the medicine for my member?
No. A band is a view of the pharmacy's own figures at the time of its last recorded stock movement, shown in as_of. Stock can change before the sale. Nothing is held or set aside. If you want the pharmacy to prepare a medicine, send a drug request, and the pharmacist decides.
Which scopes do I need for locations, exact stock and my own medicine codes?
Locations and exact stock both need inventory.stock:read. Bands need inventory.availability:read. Uploading your codes needs inventory.items:write. None of these is patient data, so no patient-data approval is needed. The pharmacy must grant each scope to your connection.
How are my HMO's own medicine codes matched to the pharmacy's items?
You upload up to 1,000 rows per request, in a body with the key entries. A row links automatically only on an exact barcode, NAFDAC, NDC or RxNorm match. A match on name alone goes to needs_review, and a person confirms it before it is trusted. A code that is not confirmed answers matched false on availability, never out of stock.
What is a drug request, and what can the pharmacist do with it?
A drug request asks a pharmacy to prepare medicines for one customer linked to your HMO. The pharmacist accepts, partly accepts, proposes a substitute, declines, marks it ready and dispenses at the till. Your portal can create, read and cancel a request and confirm or decline a substitute. A request carries no price and is not a guaranteed dispense.
How do I avoid a duplicate drug request when a call is retried?
Send an Idempotency-Key header, a unique value you choose for each request. If you retry with the same key, you get the first result and no second request. The create route refuses a request with no key. The TypeScript client lets you pass your own key when a queue may deliver a job twice.
What approvals do drug requests and dispensing history need?
They are patient data. Your organization needs a verified workspace on the Enterprise contract plan, approval for the Patients, Drug requests and Dispensing history kinds, and the accepted data-protection addendum. The pharmacy must be on the Enterprise plan, grant the scopes to your connection and record a legal basis. The customer must be linked.
Can I test this in a sandbox first?
Partly. A test key starts with ehr_test_ and reaches a sandbox of invented data. The inventory sandbox returns invented stock, and the dispensing sandbox returns two invented items and one invented flag. This guide gives no sandbox walk-through for drug requests or your own codes, so confirm those with a connected pharmacy.
Conclusion
Start with the parts that need no patient-data approval: connect, read bands and upload your codes. Add drug requests once the approvals are in place. Keep the words on your screens honest. A band is the pharmacy's last recorded figure, and only the pharmacist's decision on a drug request settles the answer.
To go further, open the developer portal. For an Enterprise contract plan or questions about a connection, contact ClinikEHR. You apply for patient-data approval in the developer portal, under Settings, then Verification. Nothing in this guide says that any HMO or pharmacy is already connected.
Connect your portal through the developer portal
Related Articles
- How Pharmacies That Serve HMO Enrollees in Nigeria Can Flag Early Refills and Duplicate Dispensing of Chronic Medication (2027)
- Running a Pharmacy That Serves HMO Enrollees in Nigeria: The Complete 2027 Guide to Chronic-Medication Refills
- What You Can Build With the ClinikEHR API in Nigeria: Ideas by Access Level (2027)
- Connecting Your HMO to the ClinikEHR API in Nigeria: Access, Approval and Data Protection Explained (2027)
- Top 10 Pharmacy Management Software in Nigeria 2027 (Updated Review)
- Stock availability and inventory (API docs)
- Drug requests (API docs)
- Drug requests in the pharmacy app (Help Center)
- ClinikEHR API documentation
- ClinikEHR pricing
Facts are as of the dates shown. Check the linked sources for updates. This is general information, not legal or professional advice.
Stay in the loop
Subscribe to our newsletter for the latest updates on healthcare technology, HIPAA compliance, and exclusive content delivered straight to your inbox.