Skip to main content

Build it well

Every call on this site can be made correctly and still leave you with an integration that fails at a busy counter. What separates the two is handling: what you check before you call, what you do with what comes back, and what the person in front of you sees while it happens.

In short

  • Check locally what can be checked locally. A round trip that fails a format rule costs the person thirty seconds and tells them nothing.
  • Read the body, not the status. ABDM returns four error shapes and only some carry a code.
  • Retry only what is safe to retry. Enrolment and one time password calls are not.
  • Say what failed and what the person can do next. A code on its own is not an error message.
  • Map every field the profile returns, not the four you need this week.

Validate before you send

These rules are the service's, and it applies them after it decrypts. Checking them in your own form turns a failed call into an inline message.

WhatRuleWhat it costs to skip
ABHA number14 digits with dashes, NN-NNNN-NNNN-NNNN, before encryption400 {"loginId": "LoginId is invalid"}
ABHA address8 to 18 characters, letters and digits with an optional . or _, then @ and the domainA rejected address after the person has chosen it
Mobile number10 digits, no country code and no +A failed call that reads as a wrong number
Aadhaar number12 digits, no spacesThe same
REQUEST-IDA fresh UUID version 4 on every callTwo calls you cannot tell apart later
TIMESTAMPISO 8601 in UTC, from a synchronised clock. Every header is in authenticationEvery call fails at once

One thing you cannot check locally is whether the account exists. A well formed ABHA number that belongs to nobody returns 404 ABDM-1114 User not found, and that is a different message to the person than a badly formed one. The plaintext shape for every encrypted field is in encryption.

Read the error, then decide

ABDM returns four different error shapes, and only two of them carry a code. Parse for all four before you write any handling, because the shape tells you where the failure came from.

Every code in the error code reference carries an action. Key your handling to that column rather than to a list of codes you maintain by hand.

ActionWhat your code does
Fix requestDo not retry. Something you sent is wrong, and sending it again will not help
Fix authFetch a fresh token, then retry once
New request idGenerate a new REQUEST-ID, then retry once
RetryBack off and retry, with a ceiling on attempts
Cannot proceedStop, and tell the person why in their own terms
Ask supportStop, and collect the ids before the context is lost
UnclassifiedTreat as Cannot proceed until you have seen it once and know better

Symptom first debugging, for the failures that produce no useful code at all, is in troubleshooting.

Retries, and the calls you must not repeat

A retry is safe when repeating the call cannot create a second thing or burn a single use value. Most of M1 fails that test.

CallSafe to repeatWhy
Session tokenYesIt issues a token and changes nothing else
Profile, card and QR code readsYesReads
Request an OTPNoRate limited. A retry loop is the usual cause of the lockout it is trying to escape
Verify an OTPNoThe OTP and the transaction id are both single use
Enrol, or create an ABHANoA success you did not see still created an account
Your callback handlerIt must beABDM repeats callbacks, so deduplicate on the id the callback repeats before you apply any effect

REQUEST-ID is the idempotency key, one fresh UUID per call, and it is what lets you tell a repeat from a new attempt in your own logs. See authentication for the headers and the gateway for the limits.

What the screen says when a call fails

The person at the counter cannot act on a code. They can act on what to do next.

What happenedWhat the screen saysWhat it must not do
The OTP did not matchSay so, keep the field, offer a resendClose the form or show a generic failure
Rate limited or locked outName the actual wait, for example thirty minutesSay something went wrong
No account foundShow it as a result, not as an errorTurn a normal empty result red
The address already existsOffer the login route insteadReport a creation failure
ABDM or the other party is not answeringSay it is not answering and to try againImply the person typed something wrong
Anything with no code at allShow the reference the person can quote to supportPrint the raw response body

The screens themselves

RuleWhy
A step happening on somebody else's device is not a spinnerFace authentication waits on a person. Say that is what you are waiting for
Show the masked destination before the waitThe search response names the mobile the OTP went to, which stops somebody waiting for a message that will never arrive
Say upfront when the account will be restrictedOtherwise the person meets the limit later, when something unrelated fails
Answer scan and share inside thirty secondsThe patient's screen is open for that long, and an acknowledgement after it shows them nothing
Offer the login methods the account actually hasauthMethods on the profile says which ones the person can use. A screen offering an Aadhaar OTP to a profile with no Aadhaar behind it fails for a reason nobody can see
Put a setting at the level it belongs toYour credentials and your callback URL belong to the integration, the facility ID and the hipId belong to each facility. A settings screen that mixes them works for the first facility and fails on the next. See one bridge, many facilities
Aadhaar numbers, one time passwords and passwords never reach a log or a databaseEncrypting a value and then logging the plain one is the same leak, moved. See encryption

The first four are journey specific and each is explained where it happens, on the M1 journeys.

Map everything ABHA gives you

The most common M1 defect is a registration form wired to four fields when the profile returned thirty. The rest is then either retyped by a person who already gave it to Aadhaar, or lost.

GET /v3/profile/account returns the fields below. Map all of them, and decide for each whether your form shows it, locks it or lets a receptionist correct it.

Profile fieldGoes toEditable in your form
ABHANumber, preferredAbhaAddressThe patient's ABDM identity, and the key you match on laterNever
name, firstName, middleName, lastNamePatient name. Keep the parts, not only the joined stringOnly on a Self-Declared profile
dayOfBirth, monthOfBirth, yearOfBirthDate of birth, assembled by youOnly on a Self-Declared profile
genderGender, as M, F or OOnly on a Self-Declared profile
mobileContact numberYes, it is the communication number and people change it
address, pincode, stateName, districtName, subdistrictName, townNameAddress. The *Code twins are the machine values, keep bothYes
stateCode, districtCode, subDistrictCodeYour own reporting, which should key on codes rather than namesNever
kycVerified, verificationStatus, verificationTypeWhether this identity was proved, and by whatNever
authMethodsWhat the person can log in with next time, so you offer the right oneNever
kycPhoto, profilePhotoIdentity photo. Decide whether you store it at all before you doNever
statusWhether the account is activeNever
localizedDetailsThe same details in the person's own language, where NHA holds themNever

kycVerified is the field the form design hangs off. A KYC verified profile was proved against Aadhaar, so its demographics are better evidence than anything typed at a desk and your form should lock them. A Self-Declared profile is what the person typed themselves, so it is correctable, and it becomes KYC verified in place if they link an ABHA number later.

Two traps. Several of these fields are nullable, and villageName, wardName and townName are commonly null, so a form that renders a blank labelled row for each is worse than one that hides them. And the profile comes back in more than one shape across M1: the update response types yearOfBirth as an integer where this one types it as a string, and the patient share payload and the PHR profile carry different names again. Map from one named endpoint and say which, rather than writing one mapper for something called the ABHA profile.

Where to go next

  • Build with AI for setting an agent up, and for how to prompt it against these rules.
  • Troubleshooting when something is already broken.
  • Go live for what certification asks of you.