How to Build an HMO Claims Integration in Nigeria With the ClinikEHR API (Step-by-Step, 2027)
A step-by-step tutorial for HMO developers: get access, look up enrollees, decide claims line by line, post payment advices and receive signed events.
By ClinikEHR Team
Duration
32 MINSYou run claims for an HMO, a health maintenance organisation that pays part of its members' hospital bills. You want your own software to read a hospital's claims, decide each line and report what you paid. This tutorial shows how to build an HMO claims integration in Nigeria with the ClinikEHR API (api.clinikehr.com), the API of the ClinikEHR app. It works through a hospital that has connected to you. ClinikAPI (clinikapi.com) is a different product, built around the FHIR health data standard, with its own site, keys and licence.
Quick Answer
To build an HMO claims integration with the ClinikEHR API, get access, let a hospital link you, then read, decide and report claims through the documented routes.
- Get access: a verified workspace on the Enterprise contract plan, a reviewer's decision and the accepted addendum.
- Connect: the hospital, on its Enterprise plan with API access on, gives you a one-time code, approves your request and links you to its record of your HMO.
- Read: list claims, read one with its lines, and look up an enrollee, a member you cover.
- Decide: adjudicate each claim line by line with an Idempotency-Key, so a retry does the work once, then post a payment advice.
- Listen: take signed events on your own webhook address, an https address ClinikEHR calls, and use the change feed to catch up after an outage.
What Is an HMO Claims API, in Plain Words
An HMO claims API lets your software read the claims a hospital sends you and answer them. A claim is the hospital's bill to you for a member's care, with one line for each service or medicine. A tariff is the price list the hospital agreed with you, and a line can carry the tariff code of its item.
You answer a claim by adjudicating it. Adjudication means deciding, line by line, what you accept. ClinikEHR's pages also call it vetting. When you pay, you post a payment advice, also called a remittance advice. It tells the hospital what you paid and against which claims.
Every insurance route needs a scope, a named permission on your key, such as insurance.claims:read. The hospital grants scopes when it approves your connection. The routes return plain JSON. They serve hospitals whose owner has set Insurance to Outside the US. That is a setting, so not every hospital outside the US is served. The routes and their documentation are published at docs.clinikehr.com.
Before You Start: The Access Path in Order
You cannot try these routes with a test key. The documentation says so:
A test key reaches only its own sandbox clinic, which holds no insurance data. Lists answer an empty page and reads answer
404.
ClinikEHR API documentation, Insurance for HMOs and insurers
The rest of the API has a sandbox, but insurance is tested against a hospital that has linked you. Every sample below needs a live key and a linked hospital, and its key is the documentation's placeholder.
Two plans must be right before your key can read a hospital's claims. They are a pair, and they are an API gate.
- Your own developer workspace is on the Enterprise contract plan. ClinikEHR sets it on request, with no self-serve checkout.
- The hospital is on the Enterprise plan, with API access switched on.
HMO billing inside the app is separate. It is available on any plan once the hospital's owner sets Insurance to Outside the US. Enterprise means the subscription plan, not the Enterprise Clinic type chosen at setup. The API add-on is for a hospital's own keys and plays no part in an organization key.
Here is the whole path, in order, with who does each step.
| # | What happens | Who does it |
|---|---|---|
| 1 | Sign up in the developer portal. A Sandbox-plan workspace and a test key exist at once. | Your developer |
| 2 | Start workspace verification: a DNS TXT record, the business profile, then Submit for review. | Your workspace Admin or Owner |
| 3 | Accept the API Terms of Use. A live key cannot be created before this. | Your Admin or Owner |
| 4 | Put the workspace on the Enterprise contract plan with Contact us to go live. | You ask, ClinikEHR sets the plan |
| 5 | Apply for patient data access for the kind Insurer claims, authorizations and enrollees. | Your Admin or Owner applies, a ClinikEHR reviewer decides |
| 6 | Accept the data-protection addendum, version 2026-10-09. | Your workspace Owner |
| 7 | Set Insurance to Outside the US under Settings, General. | The hospital's owner |
| 8 | Be on the Enterprise plan, with Allow API access switched on. It is off by default. | The hospital |
| 9 | Generate a one-time connection code and hand it to you. | The hospital's owner |
| 10 | In the portal, choose Connections, then Connect to a clinic, and enter the code. | Your developer |
| 11 | Review the request, tick only what to grant, record a legal basis and approve. | The hospital's owner |
| 12 | Link your organization to the hospital's record of your HMO. | The hospital's owner or manager |
| 13 | Create a live key and check it with GET /v1/connections. | Your developer |
| 14 | The hospital chooses ClinikEHR API as your HMO's submission channel, so claims reach you through the API. Optional: add a webhook address in the portal to receive events. | The hospital's owner or manager, and your Admin |
Step 1: Get Your Workspace Through Review
What to do. Sign up in the developer portal for a Sandbox-plan workspace and a test key. Start workspace verification: add a DNS TXT record at the host _clinikehr-verify. When the domain reads Domain verified, complete the business profile and select Submit for review.
Next, select Contact us to go live, which opens a message to ClinikEHR in your own mail client. Nothing is sent from the page. ClinikEHR sets the Enterprise plan once your organization is approved. The Essential plan, which gives live keys, never reaches patient data.
Then apply for patient data access for the kind Insurer claims, authorizations and enrollees. The application covers your purpose, the data you need, a legal basis, your regulator, two contacts and your safeguards. A ClinikEHR reviewer decides, and the documentation states no timeline. The kind holds eight scopes. This guide uses four: insurance.enrollees:read, insurance.claims:read, insurance.claims:adjudicate and insurance.remittances:write. The authorization events also need insurance.pa:read.
Last, the Owner of your workspace accepts the data-protection addendum, an agreement between Bettar Platforms, Inc. and your organization. You agree to use patient information only to administer that insurer's cover, and to report unauthorised access within 72 hours. For organizations in Nigeria, or handling the information of people in Nigeria, it also refers to the Nigeria Data Protection Act:
You are a data controller for what you receive and you will comply with the Nigeria Data Protection Act
Data Protection Addendum for Organizations, section 8.4, Bettar Platforms, Inc.
That is not legal advice. Read the addendum with your own lawyer.
- What you need: DNS access, and an Admin or Owner.
- How you know it worked: the Patient data access card on Settings, Verification reads Approved.
- The mistake people make: applying before the workspace is verified.
Step 2: Get the Hospital to Connect and Link You
What to do. You cannot find a hospital by name. Its owner generates a one-time connection code, valid from 24 hours up to 14 days as the owner chooses, and hands it to you. In the portal, choose Connections, then Connect to a clinic, and enter the code.
The owner may grant less than you asked for, and approves with a fresh second-factor sign-in. The owner also records a legal basis, which you confirm in the addendum is true for every person whose information you request.
Then the owner or a manager links your organization to its record of your HMO, under Settings, API access, Insurer links. Until then you count as a developer. Linking grants nothing by itself. The hospital can end the link at any time, and your access ends on your next request.
| The hospital's view of your organization | What it can do |
|---|---|
| An insurer or HMO: connected, permissions granted and linked to the insurer record | Read its own enrollees, plans, claims and authorization requests. Vet claims. Tell the hospital what it paid. |
| A developer: connected and permissions granted, but linked to no insurer | Submit claims for patients the hospital linked to it. It cannot read an insurer's data or act as one. |
- What you need: the hospital's owner, and rows 7 to 12 of the table done.
- How you know it worked: once you hold a live key (Step 3), a read such as the claim list answers
200instead ofhmo_not_linked. - The mistake people make: skipping the link. An unlinked organization is refused
hmo_not_linked.
Step 3: Create a Live Key and Check What It Reaches
What to do. In the portal, create a live key with an end date at most 12 months away. Keys start with ehr_live_ or ehr_test_, and every request sends one in the Authorization header as a bearer key. Keep it on your own server. Never put it in a URL, and never call the API from a browser or phone app.
Then call GET https://api.clinikehr.com/v1/connections. It lists each hospital that approved a connection to you, with its clinic_id, clinic_name, granted_scopes and expires_at. Every route below starts with https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID, using a clinic_id from that list.
Finally, ask the hospital to pick ClinikEHR API as your HMO's submission channel, so claims reach you through the API. See how claims reach HMOs.
- What you need: a verified workspace, the accepted addendum and a connection.
- How you know it worked:
granted_scopesnames the insurance scopes. - The mistake people make: using a test key and blaming the empty answers on your code.
List and Read Claims
Step 4: List the Claims Sent to You
What to do. Call GET /insurance/claims with insurance.claims:read. It lists the claims the hospital has submitted to you. Filter by status, hmo_status, enrollee_id, updated_since and insurance_company_id. A list row has no lines.
A claim has two statuses. status is the hospital's side of the lifecycle, such as submitted, denied or paid. hmo_status is your verdict: PENDING, APPROVED, PARTIALLY_APPROVED or REJECTED. That field is ClinikEHR's own.
You see only your own insurer's rows. Another insurer's record answers 404, the same as one that does not exist. A claim still in the hospital's draft stages is never shown.
Lists answer with data, has_more and next_cursor. The default limit is 25, the largest 100. For the next page, pass back the next_cursor you were given as starting_after. Never build one. There is no offset and no total.
- What you need:
insurance.claims:read. - How you know it worked: every row in
databelongs to your insurer. - The mistake people make: reading
statusas your verdict.
Step 5: Read One Claim With Its Lines
What to do. Call GET /insurance/claims/CLAIM_ID. The single read adds items, which are the lines, plus diagnoses and documents. Documents come as name, type and size only, and the files are not served.
Each line carries what was billed: qty_billed, unit_price_billed and sub_total_billed. Once you decide, it also carries what you approved: qty_approved, unit_price_approved and sub_total_approved, plus hmo_item_status. That is PENDING, APPROVED, ADJUSTED or DECLINED. A line can also carry a tariff_code and a denial_reason, and either can be empty.
The claim has totals: amount_billed, amount_approved, paid_amount and written_off_amount. A write-off is a balance the hospital closes because it will not be paid. The hospital records paid and written-off amounts, and no API call changes them.
The API does not compare a line's tariff code or price with any tariff, and no route reads a tariff. The decision is yours.
- What you need: a claim id from the list.
- How you know it worked: the answer carries
itemswith billed figures. - The mistake people make: asking for a claim the hospital has not sent. It answers
404.
Enrollee Lookup: Read the Record a Hospital Holds
An enrollee is a member you cover. A lookup reads the record the hospital holds for that member, as the hospital keyed it in. It is not live eligibility, and it asks no insurer system anything. The hospital's registration records the member number, plan and expiry by hand.
Step 6: Look Up an Enrollee by Number
What to do. Call GET /insurance/enrollees?insurance_no=ABC-001234 with insurance.enrollees:read. Send the number the patient presented. It matches the enrollee's number and their alternate number. This is the documentation's sample, with a made-up name. It needs a live key and a linked hospital.
curl "https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID/insurance/enrollees?insurance_no=ABC-001234" \
-H "Authorization: Bearer ehr_live_EXAMPLEKEYID0000000000_EXAMPLESECRET…"
{
"data": [
{
"id": "0a1b2c3d-4444-4e5f-8a6b-23456789abcd",
"insurance_company_id": "9d2e7a10-2222-4b3c-8d4e-123456789abc",
"insurance_no": "ABC-001234",
"firstname": "Ada",
"lastname": "Okafor",
"status": "AA",
"valid_until": "2027-06-30",
"plan": { "id": "7e1f0c2a-5555-4a6b-9c7d-3456789abcde", "name": "Gold", "code": "GLD" }
}
],
"has_more": false,
"next_cursor": null
}
The status codes are AA active, IA inactive, SX suspended, XX cancelled, PP pending and WP in a waiting period.
A number the hospital does not hold under your insurer returns 200 with an empty data list, not a 404. Only a single read by id answers 404. There is no route to create or change an enrollee, because only the hospital records them.
- What you need:
insurance.enrollees:read. - How you know it worked: a number the hospital holds returns one row for that enrollee.
- The mistake people make: reading it as proof of cover today.
Decide a Claim Line by Line
Step 7: Adjudicate With an Idempotency-Key
What to do. Call POST /insurance/claims/CLAIM_ID/adjudication with insurance.claims:adjudicate. Only an organization linked to an insurer can. A hospital's own key and a developer organization are refused hmo_not_linked.
The body needs a decision: approved or rejected. It sets what every line gets, approved at the billed figures and rejected at zero. Then items overrides single lines. Each item names a line by item_id, the id the line has on the claim read, and has a status: approved, adjusted or declined. adjusted approves less than billed, so set qty_approved, unit_price_approved or both. A rejection needs a reason, and so does a declined line unless the body has its own. Send a pa_code, a pre-authorization (PA) code, which an insurer issues when it agrees to a service in advance, to tie the claim to that authorization.
This is the documentation's sample, and it needs a live key and a linked hospital. It adjusts the first line to one unit and declines the second.
curl -X POST "https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID/insurance/claims/CLAIM_ID/adjudication" \
-H "Authorization: Bearer ehr_live_EXAMPLEKEYID0000000000_EXAMPLESECRET…" \
-H "Idempotency-Key: 6b9f6c1e-1d7e-4f55-9a52-0c4c3b1c8a11" \
-H "Content-Type: application/json" \
-d '{
"decision": "approved",
"items": [
{ "item_id": "LINE_ID_1", "status": "adjusted", "qty_approved": 1, "comment": "One unit covered" },
{ "item_id": "LINE_ID_2", "status": "declined", "reason": "Not covered by the plan" }
]
}'
Every write needs an Idempotency-Key header. Idempotency means that sending the same request twice has the same effect as sending it once. Make one key, such as a UUID, when you first attempt the action, never a new one per retry. A repeat with the same key returns the first answer, marked idempotent_replay: true.
The answer is the claim with its lines, and its status follows them.
| Your lines | Claim status | hmo_status |
|---|---|---|
| Every line approved exactly as billed | approved | APPROVED |
| Every line declined | denied | REJECTED |
| Anything else | partially_approved | PARTIALLY_APPROVED |
Three rules protect the hospital's books. You cannot approve more than was billed. Vetting never moves money: the body has no field for a paid or written-off amount, and sending one is refused. Once the hospital records a payment or write-off on a claim, you can no longer vet it (invalid_status_transition).
An illustrative example, with invented numbers in the hospital's own currency: you would send decision approved and one item for the medicine line, with status adjusted and qty_approved 14.
| Line | Billed | Your decision | Approved |
|---|---|---|---|
| Consultation, 1 × 5,000 | 5,000 | approved | 5,000 |
| Amoxicillin 500mg, 21 × 120 | 2,520 | adjusted to 14 units | 1,680 |
| Claim total | 7,520 | claim becomes partially_approved | 6,680 |
- What you need:
insurance.claims:adjudicateand one stored Idempotency-Key for each decision. - How you know it worked: each line shows the
hmo_item_statusand approved figures you set. - The mistake people make: swapping the two words.
rejectedis for the whole claim, anddeclinedis for one line.
Claim Events, Webhook Signing and the Change Feed
An event is a short notice that something changed. It never carries a patient's name or identifier, and never the record itself. When one arrives, you read the record. A webhook is an https address on your system that ClinikEHR calls with each event, and it is the normal way to receive them. The change feed lets you ask for events yourself, to catch up after an outage.
| Event | When it fires | What it carries |
|---|---|---|
insurance.claim.submitted | A claim reached submitted | The claim id and number, your insurer's id, both statuses, the currency and the billed, approved, paid and written-off amounts |
insurance.claim.adjudicated | A claim was vetted: approved, partly approved or denied | The same as above |
insurance.claim.paid | The hospital recorded a payment or write-off against a claim | The same as above |
insurance.pa.requested | An authorization request was sent to you | The request id and number, your insurer's id, the status, the estimated cost and the approved amount |
insurance.pa.decided | An authorization request was approved or denied | The same as above, with no pa_code |
Claim events need insurance.claims:read, and authorization events need insurance.pa:read. The decided event carries no pa_code, so read the request to get it. You receive events only for your own insurer's records, and only while your connection grants the matching permission. A hospital has events only once someone is listening: a webhook address is registered, or a connected organization has one, and the hospital's API access is on.
Step 8: Register a Webhook Address
What to do. In the portal, open the Webhooks page, which needs the Admin role or higher. Add an endpoint, copy the signing secret and send a test event. The address must be reachable over https from the public internet, and a private address such as localhost is refused. The secret is whsec_ plus 48 lowercase hex characters. It is shown once, so copy it then. Keep it on the system that checks the deliveries, never in a web page or app.
- What you need: the Admin role and a public https address.
- How you know it worked: the test event shows in your delivery log.
- The mistake people make: registering a private address.
Step 9: Check Every Delivery's Signature
What to do. Each delivery has three headers: webhook-id, webhook-timestamp, in Unix seconds, and webhook-signature. The signature header holds one or more values separated by spaces. Each value is v1, a comma, then a base64 value. The signed string is the id, the timestamp and the raw body, joined by dots:
signed_content = "{webhook-id}.{webhook-timestamp}.{raw_request_body}"
Drop the whsec_ prefix and decode the 48 hex characters to 24 raw bytes. Make an HMAC-SHA256 of the signed string, a signature made with a shared secret, using those bytes as the key. Base64-encode the result and put v1, in front of it. Compare it with each v1 value using a constant-time comparison, one that takes the same time whether or not the values match. Use the raw body, never a re-serialized copy. Reject a timestamp more than about five minutes from your clock.
While you rotate a secret, the old one keeps working for 24 hours by default, and the header carries two values. Accept a delivery if either one matches, and never require both. The TypeScript client has verifyWebhook, and the Python client has verify_webhook. See the webhook signature guide for worked examples.
- What you need: the signing secret and the raw request body.
- How you know it worked: a delivery you changed by one character fails, and an untouched one passes.
- The mistake people make: parsing the JSON first and signing what they re-serialize.
Step 10: Process Each Event Once and Catch Up With the Feed
What to do. The same event can arrive by webhook and by the feed, so process each one once. Keep the seq you last handled and skip anything at or below it. The webhook body has type, clinic_id, seq, data and created_at. The event's id is not in the body. It arrives in the webhook-id header, and a retry carries the same id.
The feed is GET /events with after_seq, which defaults to 0, and limit, from 1 to 200 with a default of 50. It does not use cursor paging. You page with after_seq and next_after. At present events are kept for 30 days, so the feed is for catching up after a short outage. The events guide has a documented catch-up loop.
- What you need:
insurance.claims:readand a storedseqfor each hospital. - How you know it worked: after an outage, the feed returns the events above your stored
seq, in order. - The mistake people make: keeping one
seqfor all hospitals. Each hospital counts its own.
Post a Payment Advice
A payment advice says: we paid this much, by this method, against these claims. It is a message to the hospital. It does not change a claim, the hospital's payments or its books. Staff record the money themselves, and only then does a claim move to partially_paid or paid. No money moves through the API.
Step 11: Tell the Hospital What You Paid
What to do. Call POST /insurance/payment-advices with insurance.remittances:write and an Idempotency-Key. This is the documentation's sample. It needs a live key and a linked hospital.
curl -X POST "https://api.clinikehr.com/v1/clinics/YOUR_CLINIC_ID/insurance/payment-advices" \
-H "Authorization: Bearer ehr_live_EXAMPLEKEYID0000000000_EXAMPLESECRET…" \
-H "Idempotency-Key: 4f1c7e0a-0b0a-4e6a-9d14-1e9b2b7f3c33" \
-H "Content-Type: application/json" \
-d '{
"insurance_company_id": "YOUR_INSURER_ID",
"paid_on": "2026-10-09",
"amount": 300,
"method": "bank_transfer",
"reference": "TRF-77821",
"allocations": [ { "claim_id": "CLAIM_ID", "amount": 300 } ]
}'
| Field | Rule |
|---|---|
allocations | Each names a claim of yours that is waiting for payment, for no more than it still owes. They cannot add up to more than amount. |
amount | Above zero. |
currency | Optional. If you send it, it must be the hospital's currency. |
method | bank_transfer, eft, cheque or other. |
reference | Up to 200 characters. |
The advice's status is advised until staff act, then recorded or declined. Staff confirm the figures, and the figures they confirm are the ones recorded, not yours. They can decline an advice that matches nothing they received. See how the hospital records HMO payments.
Illustrative: you advise 6,680 against the claim above. Posting an advice sends no event. insurance.claim.paid fires only after staff record the money, and then you read the claim for its new paid_amount.
- What you need: your insurer's id and the claim ids you are paying against.
- How you know it worked: the advice shows in the list of payment advices with
statusadvised. - The mistake people make: counting the advice as a payment. It records nothing in the hospital's books.
Curacel-Compatible Field Names
The ClinikEHR API reuses a number of the field names in Curacel's Health API, so an insurer that already speaks Curacel maps across with little work. "Curacel-compatible field names" means only that these names match. The routes, errors, idempotency rule and events are ClinikEHR's own. The Curacel side comes from Curacel's public Health API documentation, checked October 2026.
| ClinikEHR | Curacel's Health API | Same name? |
|---|---|---|
insurance_no, also the lookup parameter | insurance_no | Yes |
alternate_insurance_no | alternate_insurance_no | Yes |
Enrollee status codes AA, IA, SX, XX, PP, WP | The same six codes | Yes |
qty_billed, unit_price_billed, sub_total_billed on a line | The same three names | Yes |
qty_approved, unit_price_approved, sub_total_approved on a line | The same three names | Yes |
tariff_code on a line | tariff_code | Yes |
pa_code on a claim | pa_code on a claim request | Yes |
pa_code on a pre-authorization | code | No |
hmo_status | The claim's verdict is status, with the same four values | No, hmo_status is ClinikEHR's own |
insurance_company_id | hmo_code or hmo_id | No |
service_date_from on a submitted claim | encounter_date | No |
quantity on a submitted claim line | qty | No |
middlename, parent_enrollee_id | middle_name, parent_insurance_no | No |
benefit_label | benefit | No |
Use the Client Libraries
ClinikEHR publishes two client libraries. The TypeScript one needs Node.js 18 or later and runs on your own server. Use version 0.1.3 or later.
npm install @clinikehr/api
import { createClient } from "@clinikehr/api";
const client = createClient({ apiKey: process.env.CLINIKEHR_API_KEY! });
The client has one method for each HTTP verb. Path and query parameters go in params, and a body goes in body. It puts a fresh Idempotency-Key on every POST and reuses it on a retry.
The Python client needs Python 3.11 or later. Pin a tested range, and use the synchronous calls its guide documents.
pip install "clinikehr>=0.1,<0.2"
This guide shows curl for insurance because the documentation's client samples use other routes. Check the TypeScript guide and the Python guide first.
What the Errors Mean
An error answers as application/problem+json, with type, title, status, code and request_id. These are the ones an HMO meets. Branch on code, not on detail.
| Code | Status | What it means |
|---|---|---|
not_found | 404 | The record does not exist, belongs to another insurer, has not been sent to you, or belongs to a restricted patient. These are never told apart. |
hmo_not_linked | 403 | The hospital has not linked your organization to an insurer, or the action belongs to an insurer and you are a developer or the hospital. |
scope_missing | 403 | Your key does not carry the scope the route needs. The hospital grants scopes when it approves your connection. |
role_required | 403 | You are linked to an insurer, so you cannot submit claims or authorization requests. |
insurance_region_unsupported | 409 | The hospital uses the US insurance setup. These routes serve hospitals on the non-US setup. |
invalid_status_transition | 409 | The claim can no longer move this way, for example after a payment was recorded. |
idempotency_key_conflict, idempotency_key_reused | 409 | A key was reused for a different request, or a retry arrived while the first call was still running. |
not_verified, workspace_plan_required | 403 | A live key was used while the workspace is not verified, or is still on the Sandbox plan. |
invalid_key | 401 | The key is wrong, revoked or expired. |
rate_limited | 429 | You went over your per-minute limit. Read the Retry-After header. |
not_available, approval_required, kind_not_approved, agreement_required, legal_basis_required | 403 | A patient-data rule was not met: patient data is not open on the platform, your approval does not cover the request, the addendum is not accepted, or the hospital recorded no legal basis. |
For an organization, your workspace plan sets the per-minute limit. The documentation does not print the number. Your dashboard's Plan and usage page shows it.
Mistakes That Break an HMO Claims Integration
- Listing payment advices with a read-only key. The read route needs
insurance.remittances:write, the same scope as posting one. - Polling the feed as your main path. Each read counts against your per-minute limit and monthly allowance. Webhook deliveries do not.
- Submitting a claim as an HMO. Submitting is for developer organizations. An insurer-linked organization is refused
role_required. - Reusing one Idempotency-Key for two different decisions. The second call is refused
idempotency_key_conflict. Make one key for each decision. - Retrying a hospital that has left your connection list. A revoked connection ends on your next call, and an expired one drops off the list. Check
GET /v1/connectionsbefore you retry.
Frequently Asked Questions
How do I build an HMO claims integration with the ClinikEHR API?
First get access: a verified developer workspace on the Enterprise contract plan, a reviewer's decision on insurer data and the accepted data-protection addendum. Then a hospital on the Enterprise plan, with API access on, connects with a one-time code and links you to its record of your HMO. After that you list claims, read one with its lines, look up enrollees, decide each line with an Idempotency-Key, post payment advices and receive signed events.
What does an HMO need before it can read a hospital's claims?
Two plans must be right. Your developer workspace is on the Enterprise contract plan, which ClinikEHR sets on request, and the hospital is on the Enterprise plan with API access on. The hospital's owner must also set Insurance to Outside the US and approve the connection, and an owner or manager must link you to its record of your HMO. HMO billing inside the app is separate and is available on any plan once that setting is on.
Can I test the HMO routes with a test key?
No. A test key reaches only its own sandbox clinic, which holds no insurance data. Lists answer an empty page, and reads and writes answer 404. The rest of the API has a sandbox, but for insurance you need a live key and a hospital that has linked your organization. A test key still works for the rest of the API.
Does the enrollee lookup check live eligibility?
No. It reads the enrollee record the hospital holds for your insurer, as the hospital keyed it in, and it matches the member number or the alternate number. A number the hospital does not hold comes back as 200 with an empty list. It asks no insurer system anything, and you cannot create or change an enrollee through the API.
How do I approve or decline only part of a claim?
Post to the adjudication route with a decision of approved or rejected, which sets every line, then override single lines. Each line status is approved, adjusted or declined. Use adjusted to approve less than was billed, with a lower quantity or price. A rejection or a declined line needs a reason. You can never approve more than was billed.
Does a payment advice mark a claim as paid?
No. A payment advice is a message to the hospital that says what you paid, by which method and against which claims. It changes no claim and no books, and no money moves through the API. Hospital staff record the money, and only then does the claim move to partially paid or paid. Posting an advice sends no event.
How are claim events signed, and how long are they kept?
Each delivery carries webhook-id, webhook-timestamp and webhook-signature headers. The signature is an HMAC-SHA256 of the id, the timestamp and the raw body, made with your signing secret. Reject old timestamps and process each event once by its seq. At present events are kept for 30 days, so use the change feed only to catch up after a short outage.
Is the ClinikEHR API the same as ClinikAPI, and does it return FHIR?
No. The ClinikEHR API (api.clinikehr.com) is the API of the ClinikEHR app and works through a connected hospital. ClinikAPI (clinikapi.com) is a separate product built around the FHIR standard, with its own site, keys and licence. The HMO routes in the ClinikEHR API return plain JSON, not FHIR.
Conclusion
This integration needs an access path before any code: a verified workspace, a reviewer's decision, the addendum, and a hospital on the Enterprise plan that links you. After that the routes are small: read, decide each line with an Idempotency-Key, post advices and listen for signed events.
When you are ready, open the developer portal. For questions about access, contact ClinikEHR.
Connect through the developer portal
Related Articles
- Getting Paid by HMOs in Nigeria: The Complete 2027 Guide for Hospital Owners and Billers
- HMO Tariffs vs Hospital Prices in Nigeria: How to Bill Each HMO Line Correctly (2027)
- Pre-Authorization API for Nigerian HMOs: Approve or Deny PA Requests With a PA Code (2027)
- Connecting Your HMO to the ClinikEHR API in Nigeria: Access, Approval and Data Protection Explained (2027)
- What You Can Build With the ClinikEHR API in Nigeria: Ideas by Access Level (2027)
- ClinikEHR API documentation
- Insurance for HMOs and insurers, in the 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.