01 · Identify
This is the step where you work out who is in front of you, using whatever documentation they carry. The patient first, then whoever treats them. Each becomes an identity the whole health system agrees on.
Everything below elaborates this sentence.
What this step is
Someone arrives holding a national ID, a birth certificate, a refugee ID or a membership number. Whatever it is, this step turns it into the one identifier the health system knows them by.
Someone tells you who they are, and this step checks that against a national registry and gives back one official answer. Patients are checked against the client registry, clinicians against the health worker registry. It is the same question each time, asked of whichever register is authoritative for it.
The patient's identity is the one everything after this step keys off. The clinician's is what makes the claim defensible once care has been given.
The rule that makes it work is that an identifier without its type is not an identifier. It is only a number, and a number resolves to nothing.
Three identities get resolved here: the patient, the clinician treating them, and the facility they are in. The patient is the one that starts everything, so the rest of this section is about them. The other two follow further down.
The four moves of a lookup
The way you resolve a patient is the patient lookup, and it takes four moves:
- 01Pick the document typeThe operator chooses what they are holding: National ID, birth certificate, refugee ID. That list is fetched from the terminology service, rather than a constant in your code.
- 02Send the pairThe identifier's value and its type travel together, in one call, inside your facility context.
- 03Read back one identityThe registry resolves the pair to a single record and returns the identifier the health system knows this person by, with minimal masked details so the operator can confirm the right human.
- 04Store it and carry itSave that identifier against your own patient record. Use it for every later call in this encounter, and for every future visit by the same person.
Remember this much: an identifier travels with its type, and what comes back is stored once and reused forever.
What it looks like at the desk
Three ways to look at the same step: the screen an operator sees, the branches they can land in, and which system answers whom.
At the desk the whole step is one form: a type, a number, one button.
IDENTIFIER-TYPESFilled in, with the values a real desk would see:
type = NATIONAL_IDvaluenamematched_onregistered_onis_aliveunique_patient_idThat covers one adult with one document.
Continues onDependants and the hard casesA child with no document of their own, the six situations a front desk actually meets, and what to do when the registry will not answer.
Three identities, not one
A claim is never an assertion about one person. Every claim eventually has to say: this person, treated by that clinician, at this facility. That is three identities and three national registries, and they all resolve the same way, with an identifier plus what kind of identifier it is.
What matters more than the count is the order you build them in.
- 01First · the patientResolved at the front desk, before anything clinical or financial happens. Nothing else can start without it: there is no cover to select, no consent to take and no visit to open for someone the registry has not named. Keep .Code
- 02Then · the clinicianResolved when someone is chosen to deliver care, later in the encounter, and possibly more than once as staff change. It does not block the patient being identified, but it does block a defensible claim. Keep the and the regulator that issued it.Code
- 03Always · the facilityYou never resolve this one. Your credential already carries it, and every call above is made inside it. Keep set once in your HTTP client rather than at each call site.Code
Build them in that order. Nothing works without the patient. The clinician is what turns care already given into a claim that survives review. The facility is configuration rather than workflow.
Most integrations stop after the first, because the first is what unblocks a demo. The cost shows up weeks later, in a rejection that names a practitioner instead of a patient.
Part A is the lookup you have just read. Parts B and C follow.
Why this step exists
Everything downstream keys off one value, unique_patient_id: cover selection, eligibility,
authorisation, preauthorisation, reservation, billing and claims. This step is where that value
comes from, and it is the only place it comes from.
Lookup is kept separate from eligibility on purpose. A member holding three covers should not wait on three balance checks before anyone has asked which cover they are using today. So this call stays light, with enough to render a chooser and nothing that needs a balance.
Consent is not asked here. Resolving an identity is not the same as reading a record, and the two are separated on purpose: proving the member agreed is 3 · Consent, once a cover is in view and there is something to consent to. What this call returns is minimal and masked, enough to confirm you have the right human in front of you without handing over the record.
The call
Two calls make this step. One tells you which document types are currently accepted, the other resolves the pair. Both live in the reference, with every parameter, every response code, and a Try it console for when the sandbox is live.
| Call | What it does | What you keep |
|---|---|---|
GET · identifier types | The document types a desk may choose from today. Fetch and cache it; never hard-code it. | code, version |
POST · lookup | Resolves the identifier pair to one registry identity, inside your facility context. | unique_patient_id |
Open 01 · Identify in the Rail API reference to read or run them. The sidebar there is this same rail, 00 through 08, so the stop you are reading and the endpoints you are calling stay in step. Connect once with the credentials widget in the header and every call on that page becomes runnable.
The document types are not part of the rail's API at all. They are a coded list on the terminology service, and this is the call:
IDENTIFIER-TYPESThe document types a front desk may choose from: national ID, birth certificate, refugee ID, and the rest. The list changes as types are added and withdrawn, so a hard-coded copy is correct the day you write it and quietly wrong months later.
curl -sS "https://ilm-dev.dha.go.ke/dev/ts/api/v2/concepts/?system=IDENTIFIER-TYPES&version=serving&page_size=50" \
-H "authorization: Bearer $TS_TOKEN"code, show display, cache on the returned version. version=serving pins the latest approved release.Fetching and exports →Three things the reference will not tell you, which is why this page exists.
| Call | What it does | What you keep |
|---|---|---|
Send the pair | A value without its type resolves to nothing. The reference lists both fields; only this page says why sending one is the commonest false negative. | type + value |
Keep one field | Every operation in the reference names the field worth persisting. Store it against your own patient record the first time you see it. | unique_patient_id |
Fetch, never embed | The type list moves. A constant in your code is correct the day you write it and quietly wrong months later. | the cached version |
What you keep
| Call | What it does | What you keep |
|---|---|---|
POST /api/v2/patients/lookup | Resolves the person to one identity, with masked details to confirm them | unique_patient_id |
GET /api/v1/terminology/concepts?code_system=IDENTIFIER-TYPES | Tells you which identifier types are currently accepted | codes[].code |
GET /api/v1/professionals | Resolves the attending clinician and returns a licence verdict | licence.status |
unique_patient_id is the value that ties the rest together. Store it against your own patient
record the first time you see it. Everything after this step takes it as input, and re-resolving somebody you have
already resolved is the largest source of avoidable delay at a front desk.
Keep lookup_ref too, for the length of the interaction. It is the audit handle for this
resolution, and echoing it on a retry de-duplicates the attempt rather than recording a second
one.
A · The patient
Covered above: an identifier pair resolves to one registry identity, and every later call hangs off it.
One detail belongs here. The lookup also returns masked verified contacts, the phone and email the member has actually confirmed. Those are the channels a one-time code goes to at 3 · Consent. When someone says "the code never arrived", start here. An unverified channel is a dead end rather than a delay.
One habit is worth more than the rest: never store a name as free text where the registry gives you an identifier. A typed name is only a label. The registry identifier is evidence, and evidence is what you get asked for months later.
B · The healthcare worker
Every billed line carries the clinician who performed it, their licence, and the body that regulates them. Resolving that here makes the line defensible later, and it is the last cheap moment to discover a lapsed licence.
The question is the same one asked of the patient, who are you and does your paperwork hold up, and it is answered the same way: a number plus what kind of number it is. The regulator is half of a registration number, exactly as a document type is half of a patient identifier.
It happens at a different moment, though, and usually on a different screen: the patient is resolved at the desk, the clinician when care is assigned.
Continues onResolving the clinicianThe regulator picker, what a licence verdict looks like when it holds and when it does not, cadres and specialities, and the four things the health worker registry buys you, with the screens, the sequence and the workflow.
C · The facility
Proving it is the same person
Resolving an identifier tells you a record exists. It does not tell you the person holding the document is the person in the record. That proof happens at 03 · Consent, where you send a purpose and the rail decides the factor. What is possible there is decided here, by flags this step returns, so it belongs on this page.
There are two paths, and they differ in kind rather than in degree.
| Call | What it does | What you keep |
|---|---|---|
Adults | Verified against the national biometric database. Those prints were captured by the state, not by you, so template age and quality are outside anyone's control. | match or no match |
Minors, 7 to 17 | No national record exists, so they are enrolled at the point of care and the prints are sent to the biometric service. This is the only path where the prints originate with the provider. | enrolment outcome |
Because minors are enrolled rather than matched, not every desk is an enrolment point. If your integration serves paediatrics, find out whether the sites you are deploying to can enrol before you assume the fingerprint path exists for them at all.
What breaks here
Identity failures rarely announce themselves where the mistake was made. They surface later disguised as something else, usually as "the patient's fault" or "the payer's fault".
Patient identity
| Where it surfaces | What the desk sees | What actually went wrong |
|---|---|---|
| Lookup | "This patient is not in the system" | The identifier was sent without its type, or with the wrong type. Genuine absence is rare; a mismatched type is not. |
| Lookup | "It worked last month and now it does not" | A hard-coded identifier type list. A type was withdrawn or renamed upstream, and the picker is still offering the old code. |
| Cover selection | "They definitely have cover, but nothing shows" | Resolved to the wrong person. The identifier matched more than one record and was silently disambiguated instead of being sent back for a second document. |
| Cover selection | "The system says not covered, so we sent them away" | A cover result treated as a lookup failure. The identity resolved; only the cover did not, and the reason and possible solution were dropped instead of shown. |
| Consent | "The OTP never arrives" | The contact on the registry is stale or unverified. The masked contacts returned at lookup are what consent will actually use, so offer only verified ones. |
| Anywhere mid-encounter | "The same patient is on the bill twice" | The patient was re-resolved partway through instead of the identity being carried forward, producing two identifiers for one person. |
| Front desk queue | "Registration is slow at peak" | Every visit triggers a fresh lookup. A returning patient should be read from your own record, because the registry identifier is stored once and reused for life. |
| Claim | "Rejected, and we cannot see why" | A name was stored as free text at intake instead of the registry identifier. A typed name is a label; the registry identifier is the evidence. |
Clinician identity
| Where it surfaces | What the desk sees | What actually went wrong |
|---|---|---|
| Consent | "The scan keeps failing for this patient" | Adult prints are matched against the national database, and roughly three in ten do not match. This is the expected path rather than a fault, so fall back to a one-time code. |
| Consent, paediatrics | "There is no fingerprint on file for this child" | Minors have no national record and must be enrolled at the point of care. Not every site is an enrolment point. |
| Clinician selection | "That registration number does not exist" | The wrong regulator. The same digits can be valid under KMPDC and under another council, and the number is meaningless without the body that issued it. |
| Billing | "Line rejected: practitioner not eligible" | A lapsed or suspended licence, or the wrong speciality for the intervention. Licence status is checked live at billing, and checking it at selection turns this into a fixable conversation. |
| Billing | "We cannot say who performed this" | The clinician was picked from a typed internal list rather than the registry, so the line carries a name but no defensible registration. |
Two patterns account for most of those rows. The first is an identifier sent without its type: a patient number missing its document type, a registration number missing its regulator. The second is a state that exists in the API but not on the screen: a conflict, an unusable cover, a request nobody has sent yet. Neither is hard to fix, and both are expensive to find at a claim.
Errors
| HTTP | error | When |
|---|---|---|
| 404 | MEMBER_NOT_FOUND | The identifier does not resolve to any registry record. |
| 409 | IDENTIFIER_CONFLICT | It matches more than one record and cannot be disambiguated, so collect a second identifier. |
A 409 is not a failure to retry. It is a prompt to ask the person at the desk for something else.
What carries forward
You leave this step holding three things. Each is an input somewhere later, and losing one means coming back for it, usually at the worst moment.
| Call | What it does | What you keep |
|---|---|---|
unique_patient_id | The patient's identity. Taken as input by cover selection, consent, the visit, preauthorisation, billing and the claim. | Store on your patient record, permanently |
Registration number | The attending clinician, with the regulator that issued it. Every billed line carries it; preauthorisation re-checks the licence. | Store on the encounter |
facility_code | Where this is happening. It is already in your credential, but it scopes every rule that follows, including pricing. | Set once in your HTTP client |
The rail tells you where to go next. Every response carries next_action: switch on its
type, then use the endpoint it hands you instead of building one, because it already carries
the identity you just resolved.
Code
Constructing that URL yourself works right up until a path changes. Reading it from the response never breaks, and it lets the sequence be reordered without touching your code. The full contract is at next_action.
Next: 02 · Verify cover. With an identity resolved, the question becomes which cover pays.
A complete brief for this step — the calls to make, what to persist, and every failure path to handle. Nothing on this page is assumed.

