HPR ID & Authentication
Create a Health Professional Registry ID from Aadhaar, find an existing one, verify the communication mobile, and log in to obtain the HPR user token.
An HPR ID (also called HPID) is a health professional's Aadhaar-verified digital identity — the professional equivalent of an ABHA. Everything else in Milestone 4 depends on it: professional registration writes against it, and facility onboarding is performed by a facility manager who holds one.
This page covers three things:
- Create an HPR ID from a verified Aadhaar.
- Find an existing HPR ID so you don't create duplicates.
- Log in to get the HPR user
token (
x-hprid-auth/hprToken) that registry writes require.
Prerequisites
All calls below send Authorization: Bearer $SYSTEM_TOKEN (the
gateway/system token from M4 setup).
Aadhaar number, OTP, mobile, email and password are RSA-encrypted — see
Data encryption.
Create an HPR ID
Aadhaar is no longer verified with a generateOtp / verifyOTP OTP pair.
Instead you generate a one-time Aadhaar link, open it in the professional's
browser, and poll isAuthenticated until they finish. Once authenticated,
verifyOTP (now just { txnId }, no OTP) returns the verified demographics.
The flow is: generate the Aadhaar link → open it in the browser → poll for completion → fetch verified demographics → check for an existing account → pick a username → verify the communication mobile (if it differs from the Aadhaar mobile) → create the account.
sequenceDiagram
participant App as Your app
participant User as Professional (browser)
participant HPR as NHPR API
App->>HPR: POST /aadhaar/generateLink
HPR-->>App: { txnId, url }
App->>User: Open url in browser
User->>User: Aadhaar consent + OTP on ABDM gateway
loop until true or timeout
App->>HPR: POST /aadhaar/isAuthenticated { txnId }
HPR-->>App: false
end
App->>HPR: POST /aadhaar/isAuthenticated { txnId }
HPR-->>App: true
App->>HPR: POST /v2/registration/aadhaar/verifyOTP { txnId }
HPR-->>App: verified demographics
App->>HPR: POST checkHpIdAccountExist / suggestion / createHprIdWithPreVerifiedGenerate the Aadhaar link
Generate a one-time Aadhaar authentication link scoped for NHPR registration.
Returns a txnId and a url; the link is valid for 5 minutes (regenerate
if it expires).
/aadhaar/generateLinkReturns a txnId and an Aadhaar auth url (host
healthidbeta.abdm.gov.in). Carry the txnId through
every subsequent step.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/aadhaar/generateLink" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scopes": ["nhpr-register"],
"source": "NHPR"
}'{
"txnId": "ece312f8-6e22-4d62-9dc1-...",
"url": "https://healthidbeta.abdm.gov.in/abdm/aadhaar/gateway/auth?link_id=xxxx"
}Scope & source changed
The correct body is { "scopes": ["nhpr-register"], "source": "NHPR" }.
Older integrations that sent ["hpid"] / a custom source will
fail against the current NHPR flow.
Open the link in the user's browser
Redirect the professional to the returned url (host
healthidbeta.abdm.gov.in). They give Aadhaar consent and complete the OTP
on the ABDM Aadhaar gateway — your app never handles the Aadhaar OTP. Open
it in a new tab / in-app browser and keep the txnId to poll against.
// url + txnId came from generateLink
window.open(url, "_blank"); // or redirect the current tabPoll the authentication status
After redirecting, poll isAuthenticated with the txnId on a short interval
until it returns true (or you hit a timeout). The response is a bare
boolean, not an object.
/aadhaar/isAuthenticatedReturns true once the professional has completed Aadhaar
verification for this txnId, false otherwise. Poll
in a loop with a delay (e.g. every 3s) until true or the 5-min
link expires.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/aadhaar/isAuthenticated" \
-H "Authorization: ******" \
-H "Content-Type: application/json" \
-d '{ "txnId": "TXN_ID" }'false // keep polling
// ...once the user finishes on the gateway:
trueasync function waitForAadhaar(txnId, { intervalMs = 3000, timeoutMs = 300000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(`${BASE}/aadhaar/isAuthenticated`, {
method: "POST",
headers: { Authorization: SYSTEM_TOKEN, "Content-Type": "application/json" },
body: JSON.stringify({ txnId }),
});
if ((await res.json()) === true) return true; // done
await new Promise((r) => setTimeout(r, intervalMs));
}
throw new Error("Aadhaar link expired or not completed in time");
}Fetch verified demographics
Once authenticated, exchange the txnId for the KYC demographics. verifyOTP
no longer takes an OTP — the OTP was handled in the browser link, so the body
is just { txnId }.
/v2/registration/aadhaar/verifyOTPReturns the verified name, masked mobile, photo, DOB and address from Aadhaar KYC. Use these to prefill the create-HPR-ID form.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v2/registration/aadhaar/verifyOTP" \
-H "Authorization: ******" \
-H "Content-Type: application/json" \
-d '{ "txnId": "TXN_ID" }'{
"txnId": "563109c8-...",
"mobileNumber": "******5126",
"photo": "<base64>",
"name": "Rahul Sharma",
"gender": "M",
"dob": "1990-01-01",
"address": {
"house": "12A",
"street": "MG Road",
"district": "Bangalore",
"state": "Karnataka",
"pincode": "560001"
}
}verifyOTP is now OTP-free
In the current NHPR flow verifyOTP carries only
{ "txnId" } — matching the HPID/HPR Swagger. The old
generateOtp → verifyOTP (with an RSA-encrypted
otp) pair has been removed; the Aadhaar OTP is entered by the
user on the gateway page instead.
Check for an existing account
Before creating anything, confirm this Aadhaar doesn't already have an HPR ID.
/v1/registration/aadhaar/checkHpIdAccountExistIf an account exists it returns the existing HPR ID and demographics with
"new": false — stop and route the user to
login. See
Find an existing HPR ID for
the full response.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v1/registration/aadhaar/checkHpIdAccountExist" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "txnId": "TXN_ID" }'Swagger difference
The HPID Swagger models this body as { "txnId", "preverifiedCheck" }.
The NHPR PDF sends only { "txnId" }. Both are accepted in the
sandbox; add preverifiedCheck if you hit a validation error.
Get HPR ID suggestions
/v1/registration/aadhaar/hpid/suggestionSuggests available username-style HPR IDs from the KYC name. The user picks
one (or supplies their own) for createHprIdWithPreVerified.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v1/registration/aadhaar/hpid/suggestion" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "txnId": "TXN_ID" }'{ "hprIdSuggestion": ["asha.kumari", "ashakumari19", "asha.k1990"] }Verify the communication mobile
Skip this if the professional's mobile matches the Aadhaar-linked number
(check with demographicAuthViaMobile).
Otherwise send and verify an OTP.
/v1/registration/aadhaar/generateMobileOTPSends an OTP to the communication mobile.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v1/registration/aadhaar/generateMobileOTP" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "mobile": "<RSA-encrypted mobile>", "txnId": "TXN_ID" }'/v1/registration/aadhaar/verifyMobileOTPConfirms the OTP.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v1/registration/aadhaar/verifyMobileOTP" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "otp": "<RSA-encrypted otp>", "txnId": "TXN_ID" }'Create the HPR ID
Create the account from the pre-verified Aadhaar transaction. email and
password must be RSA-encrypted; profilePhoto is a base64 image string.
/v2/registration/aadhaar/createHprIdWithPreVerifiedReturns the professional's HPR ID (number + username) and an HPR user token.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v2/registration/aadhaar/createHprIdWithPreVerified" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"txnId": "TXN_ID",
"email": "<RSA-encrypted email>",
"idType": "hpr_id",
"domainName": "@hpr.abdm",
"firstName": "FirstName",
"middleName": "",
"lastName": "LastName",
"password": "<RSA-encrypted password>",
"profilePhoto": "<base64 image>",
"hprId": "abcXXXX19",
"sourceType": "AADHAAR",
"hpCategoryCode": 6,
"hpSubCategoryCode": 33,
"clientId": "",
"stateCode": "27",
"districtCode": "472",
"council": false,
"role": 3
}'{
"token": "eyJhbGciOiJSUzUxMiJ9...",
"hprIdNumber": "71-XXXX-XXXX-XXXX",
"hprId": "abcXXXX19@hpr.abdm"
}Field rules (from the HPR workbook)
hprId (username): at least 4
characters; letters and digits only, plus a dot (.) — no
other special characters. Use the
suggestion API for at least 3 options.
email: must contain @, a valid
domain, and no spaces (encrypt before sending).
password: minimum length 8; at least one
uppercase, one lowercase, one number and one special character; no consecutive
(conjugate) digits or letters; must not contain the first or last name; the
UI's confirm-password must match.
Aadhaar & OTP validation
A valid Aadhaar is a 12-digit number and the Aadhaar OTP is a 6-digit number — both are validated on the ABDM gateway during the link step, so your app no longer collects them directly.
Category, sub-category & role codes
hpCategoryCode, hpSubCategoryCode and role are code values. Categories and
roles are small fixed lists; fetch the authoritative sub-category list from the
masters API, but the values used by the create-HPR-ID API are:
hpCategoryCode | Category |
|---|---|
1 | Doctor |
2 | Nurse |
6 | Pharmacist |
role | Meaning |
|---|---|
1 | Healthcare Professional |
2 | Facility Manager |
3 | Healthcare Professional and Facility Manager |
hpSubCategoryCode | Sub-category | For |
|---|---|---|
1 | Modern Medicine | doctor |
2 | Dentist | doctor |
3 | Ayurveda | doctor |
4 | Unani | doctor |
5 | Siddha | doctor |
6 | Homoeopathy | doctor |
89 | Sowa-Rigpa | doctor |
220 | Yoga and Naturopathy | doctor |
7 | Registered Auxiliary Nurse Midwife (RANM) | nurse |
8 | Registered Nurse (RN) | nurse |
9 | Registered Nurse and Registered Midwife (RN & RM) | nurse |
10 | Registered Lady Health Visitor (RLHV) | nurse |
33 | Pharmacist | pharmacist |
Two different sub-category tables
These sub-category codes are the ones for createHprIdWithPreVerified. The professional registration API uses a different sub-category numbering — don't reuse codes across the two APIs. Prefer the masters API in both cases.
Find an existing HPR ID by Aadhaar
Use this to recover a professional's existing HPR ID (and rich demographics)
straight from their Aadhaar. It reuses the same link + poll authentication
as account creation, then reads the result from checkHpIdAccountExist.
Generate the Aadhaar link
/aadhaar/generateLinkReturns a txnId and Aadhaar auth url.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/aadhaar/generateLink" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scopes": ["nhpr-register"],
"source": "NHPR"
}'{
"txnId": "143ec756-d312-4a0a-b833-b4202fc394d9",
"url": "https://healthidbeta.abdm.gov.in/abdm/aadhaar/gateway/auth?link_id=xxxx"
}Open the link and poll
Redirect the user to url, then poll
isAuthenticated with the txnId until it
returns true — exactly as in Create an HPR ID. Your app
never handles the Aadhaar OTP; it is entered on the ABDM gateway page. When
authenticated, read the account straight from checkHpIdAccountExist below.
Check whether an HPR ID exists
/v1/registration/aadhaar/checkHpIdAccountExist"new": false ⇒ this Aadhaar already has an HPR ID (returned in
hprId / hprIdNumber). "new": true ⇒ no
account yet, proceed to create one.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v1/registration/aadhaar/checkHpIdAccountExist" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "txnId": "TXN_ID" }'{
"token": "eyJhbGciOiJSUzUxMiJ9...",
"hprIdNumber": "71-2665-5777-2713",
"hprId": "anujsharma1990@hpr.abdm",
"categoryId": 100,
"subCategoryId": 85,
"name": "Anuj Sharma",
"firstName": "Anuj",
"lastName": "Sharma",
"gender": "M",
"yearOfBirth": "1990",
"monthOfBirth": "11",
"dayOfBirth": "1",
"stateCode": "9",
"districtCode": "145",
"stateName": "Uttar Pradesh",
"districtName": "Ghaziabad",
"pincode": "201005",
"address": "C/O ... Sahibabad",
"profilePhoto": "<base64>",
"mobile": null,
"new": false
}Verify the mobile matches Aadhaar
To decide whether a communication mobile needs its own OTP, ask the registry whether the number is the Aadhaar-linked one via a demographic auth.
/v2/registration/aadhaar/demographicAuthViaMobile{ "verified": true } means the mobile is the Aadhaar-linked
number and no separate mobile OTP is needed. If it returns
false, fall back to the
mobile OTP flow.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/v2/registration/aadhaar/demographicAuthViaMobile" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"txnId": "TXN_ID",
"mobileNumber": "<RSA-encrypted mobile>"
}'{ "verified": true }Log in to get the HPR user token
Registry writes act on behalf of the professional, so they need that user's
HPR token (sent as x-hprid-auth, or hprToken / hpr_token in the
body). A returning user obtains it by logging in one of three ways. Every login
call still carries your Authorization: Bearer $SYSTEM_TOKEN.
/api/v1/auth/authPasswordThe simplest path — HPR ID + password returns the user token.
curl -X POST "https://apihspsbx.abdm.gov.in/v4/int/api/v1/auth/authPassword" \
-H "Authorization: Bearer $SYSTEM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"idType": "hpr_id",
"domainName": "@hpr.abdm",
"hprId": "username@hpr.abdm",
"password": "<RSA-encrypted password>"
}'{
"token": "eyJhbGciOiJSUzUxMiJ9...",
"expiresIn": 1739710198,
"refreshToken": null
}Where the token goes next
Hold on to this token. Professional register/update and the
email/mobile verification APIs take it in the body as
hprToken / hpr_token; HFR
basic-information and submit-facility take it as the
x-hprid-auth header.
Sources
- ABDM NHPR — Register Professional & Create HPR ID (v2.0, 22-06-2026)
- ABDM NHPR — Search Facility & Find HPRID by Aadhaar
- ABDM NHPR — Update Professional (login/auth section)
- ABDM HPR Test Cases workbook (field validations)
- HSP Swagger — HPID / HPR OpenAPI specs (ground truth)
