ABDM v3 · M1–M4 implemented

Guide ABDM protocol

How ABDM integration works, end to end

Most of ABDM is not request and response. It is a request, a 202, and an answer that arrives later on a URL you registered — followed by consent artefacts, encrypted pushes and FHIR documents. Here is the whole path, in the order a real integration meets it.

01The shape of it

If you are new to ABDM, what ABDM is covers the ecosystem. This guide assumes you know what ABHA, HFR, a HIP and a HIU are, and want to know what the software actually does.

An integration talks to three kinds of ABDM endpoint:

  • The ABHA service, for creating and verifying patient identities. Synchronous: you call, you get the answer.
  • The registries, HFR and HPR, for facilities and professionals. Also synchronous, on their own host.
  • The gateway in front of the consent manager (HIE-CM), for linking records, consent and data exchange. Asynchronous.

The gateway is asynchronous because most of what passes through it involves someone else: the consent manager, another provider, or a patient deciding in an app. So a call to the gateway is acknowledged immediately, usually with 202 Accepted and no useful body, and the real answer arrives later as an HTTP request from ABDM to you. In the other direction, ABDM calls you when a patient looks for their records or a HIU asks for them, and you answer by calling the gateway back.

That inversion is the defining fact of the integration. Your system is a client and a server at once, and it has to remember what it asked for.

02Environments and session tokens

NHA runs a sandbox and production, with separate credentials. Registering software in the sandbox gives you a client id and secret. The gateway base URLs include an /api/hiecm prefix that several published guides leave out:

sandbox     https://dev.abdm.gov.in/api/hiecm
production  https://apis.abdm.gov.in/api/hiecm

Every gateway call is authorised with a session token, minted from your client credentials:

POST {base}/gateway/v3/sessions

{
  "clientId": "SBX_...",
  "clientSecret": "...",
  "grantType": "client_credentials"
}

The token is short-lived. Cache it and reuse it until close to expiry; do not mint one per request. This matters more than it looks, because ABDM throttles token minting and the throttle is silent: a burst of session requests is answered with 401 and an empty body for several minutes. A genuinely wrong secret looks different — a 401 with a JSON error. Code that treats every 401 as “refresh the token and retry” turns the first into a long outage.

03Headers on every gateway call

Authorization: Bearer <session token>
REQUEST-ID:    <uuid>
TIMESTAMP:     2026-09-07T12:00:00.000Z
X-CM-ID:       sbx          (abdm in production)
X-HIP-ID:      <facility HIP id>    on HIP-role calls
X-HIU-ID:      <facility HIU id>    on HIU-role calls

Each has a way of going wrong:

  • REQUEST-ID is a UUID you generate. It is also how ABDM de-duplicates, so a retry of the same operation should reuse the same id, not mint a new one — otherwise a timeout-and-retry can create two consent requests.
  • TIMESTAMP is UTC ISO 8601 with milliseconds and a trailing Z. A timestamp without the milliseconds, or with an offset instead of Z, is rejected.
  • X-CM-ID names the consent manager, and differs by environment. Hard-coding sbx is a classic go-live bug.
  • X-HIP-ID or X-HIU-ID identifies the facility the call is made for, in the role it is acting in. Send exactly one. A facility that is both HIP and HIU still sends one per call, chosen by the flow.

Header casing matters to the gateway. A few flows add their own headers, such as X-LINK-TOKEN for HIP-initiated linking.

04The bridge URL and callbacks

The bridge URL is the public HTTPS address where ABDM delivers callbacks. You register it once per client with PATCH /gateway/v3/bridge/url (or in the sandbox portal, if the call is blocked). The gateway then appends each callback’s own path to it. If you register https://hmis.example.in/abdm, a consent notification for a HIU arrives at https://hmis.example.in/abdm/api/v3/hiu/consent/request/notify.

A callback endpoint has four jobs, in this order:

  1. Authenticate. Callbacks carry a bearer JWT signed by ABDM. Verify its signature and expiry against ABDM’s published JWKS. That key set is not under the v3 prefix — in the sandbox it is at /gateway/v0.5/certs, and /gateway/v3/certs returns 404. Get this wrong and every callback fails authentication while your own logs show nothing obviously misconfigured. With verification switched off instead, anyone who finds the URL can forge a consent grant.
  2. Persist. Store the raw callback before doing anything with it. ABDM delivers at least once, so de-duplicate on the path plus the request id, and keep the record for replay and for support cases.
  3. Acknowledge quickly. Return promptly and do the work on a queue. A large FHIR transfer should never delay an acknowledgement.
  4. Correlate. Match the callback to the operation that caused it. Callbacks carry the original request id in response.requestId; you need to have stored it against your own record — the patient, the link attempt, the consent request — when you made the call.

Answers to ABDM-initiated requests follow the same pattern in reverse. When ABDM sends .../care-context/discover, you reply by calling .../on-discover on the gateway, quoting its request id. The on- prefix always means “this is the answer to that”.

05ABHA calls work differently

The ABHA APIs (M1) are on their own host and are synchronous. Their peculiarity is field-level encryption: Aadhaar numbers, OTPs and some other identifiers must be sealed before sending, with ABDM’s RSA public key from /v3/profile/public/certificate. The padding is specifically RSA/ECB/OAEPWithSHA-1AndMGF1Padding — OAEP with SHA-1 for both digests. PKCS#1 v1.5 and OAEP-SHA-256 are both rejected. (SHA-1’s weakness is collisions, which OAEP does not rely on; either way, the counterparty fixes the scheme.)

ABHA flows also juggle tokens beyond the session token, and confusing them is the usual cause of an unexplained 401:

  • A T-token is a short-lived transaction token. A mobile OTP login that matches several ABHAs returns one; it is only good for choosing which account to continue with.
  • An X-token is the citizen’s own token after a successful login, used to read their profile, card and QR.

Treat a T-token as an X-token and every profile read fails. Store neither longer than the session needs, and never store Aadhaar numbers or OTPs at all. The ABHA integration page covers the enrolment routes.

06Facility registration and bridge linkage

Before a facility can link or share anything, two things must be true on ABDM’s side. The facility must exist in HFR, which is where its HIP and HIU ids come from. And its HIP/HIU services must be registered against your bridge — the step that tells ABDM that this facility’s traffic legitimately arrives through your software.

The second step is the one teams miss. Without it, the facility is in HFR, your session token is valid, your headers are right, and linking calls are still rejected — often as a 403 or with no callback — with nothing in the error naming the cause. For a multi-tenant HMIS it has to be done per facility as each customer goes live. The HFR integration page walks through registration.

07Linking care contexts

A care context is one episode of care — an OPD visit, an admission, a lab order — that a HIP holds records for. Linking attaches it to a patient’s ABHA so it shows in their PHR app and can later be shared. There are two ways it happens.

HIP-initiated linking

The facility already has the patient’s ABHA address on file, for example from registration, and links new records as they are created:

HIP  → /v3/token/generate-token         abhaAddress, name, gender, yearOfBirth
     ← .../hip/token/on-generate-token   (callback: link token)
HIP  → /hip/v3/link/carecontext          X-LINK-TOKEN + care contexts
     ← .../link/on_carecontext           (callback: linked)
HIP  → /hip/v3/link/context/notify       new records exist

Note that the token request carries demographics alongside the ABHA address, and that two callbacks separate the start from the finish. Both have to be correlated back to the right patient and visit.

User-initiated linking

The patient, in their PHR app, searches for a facility and asks whether it holds their records. Here ABDM calls the HIP:

ABDM → .../hip/patient/care-context/discover   verified identifiers, demographics
HIP  → .../on-discover                          matching care contexts, or none
ABDM → .../hip/link/care-context/init          the contexts the patient chose
HIP  → .../on-init                              HIP sends the patient an OTP
ABDM → .../hip/link/care-context/confirm       the OTP the patient entered
HIP  → .../on-confirm                           linked

Discovery matching deserves more care than it usually gets. A verified ABHA address is a conclusive match. A mobile number shared by a family is not. When a match is ambiguous, answer that nothing was found rather than guessing: a false positive attaches one person’s records to another’s ABHA, which is the worst outcome the system can produce.

A HIU that wants records starts with a consent request. It names the patient’s ABHA address, the requester, a purpose, the health information types wanted (OPConsultation, Prescription, DiagnosticReport and so on), the date range of records, and how long the data may be kept.

HIU  → /consent/v3/request/init
     ← .../hiu/consent/request/on-init    ABDM's consent request id
          ... the patient decides, in minutes or days ...
     ← .../hiu/consent/request/notify     GRANTED or DENIED, artefact ids
HIU  → /consent/v3/fetch                 per artefact
     ← .../hiu/consent/on-fetch          the artefact's actual terms

Fetch the artefact rather than assuming it matches the request. The patient can approve a narrower scope than was asked for, and the artefact is the only statement of what was agreed. It is signed by the consent manager, and there is one per HIP involved.

On the other side, each HIP receives a consent notification at .../consent/request/hip/notify carrying the artefact that concerns its records, and acknowledges it with on-notify. A HIP sees granted, revoked and expired artefacts; it never sees the original request. Store the artefact: it is what the HIP checks when data is requested.

09Transferring the records

With a granted artefact, the HIU asks for the data:

HIU  → /data-flow/v3/health-information/request
         consent artefact id, date range,
         dataPushUrl, keyMaterial (public key + nonce)
     ← .../hiu/health-information/on-request

ABDM → HIP .../hip/health-information/request
HIP  → .../health-information/hip/on-request
HIP  → POST dataPushUrl            encrypted FHIR bundles, HIP's key material
HIP  → .../health-information/notify    TRANSFERRED or FAILED
HIU  → .../health-information/notify    what it received

Three details shape the implementation.

  • The records bypass the gateway. The HIP posts directly to the dataPushUrl the HIU advertised. The gateway only carries the messages about the transfer. The HIU’s push endpoint is necessarily unauthenticated by ABDM tokens, so make its URL unguessable per request and rate-limit it.
  • A request is not an authorisation. The HIP should re-check the artefact at transfer time — still granted, not revoked, not expired — and select only the care contexts whose health information types and dates fall inside it.
  • Partial success is reported per context. The session status is TRANSFERRED or FAILED; a transfer where one context could not be built is still TRANSFERRED, with that context marked errored in the per-context statuses.

The HIU decrypts, validates and stores what arrives, and must honour the artefact’s data-retention date and any later revocation. The HIP and HIU pages go deeper into each side, and HIP vs HIU helps decide which you need.

10Fidelius encryption

Health data is end-to-end encrypted between HIP and HIU with a scheme ABDM calls Fidelius. Neither the gateway nor the consent manager holds a key that can open it.

  1. Each side generates a fresh key pair for this transfer and a random 32-byte nonce. The HIU sends its public key and nonce in the request; the HIP sends its own with the pushed data.
  2. Each side computes an ECDH shared secret from its own private key and the other’s public key.
  3. The two nonces are XORed. The first 20 bytes of the result are the salt; the last 12 bytes are the AES-GCM IV. XOR is symmetric, so both sides arrive at the same values without exchanging them.
  4. HKDF-SHA256 derives a 32-byte key from the shared secret and the salt.
  5. The FHIR bundle is sealed with AES-256-GCM, with no associated data. The tag authenticates the content: tampered or mis-keyed ciphertext fails to open rather than decrypting to garbage.

Why “Curve25519” does not mean X25519

The key material says Curve25519, and most developers reach for their language’s X25519 function. That is the wrong function. ABDM’s reference implementations use the Java BouncyCastle library, which represents Curve25519 in short-Weierstrass form — the same mathematical curve written in different coordinates.

That has two visible consequences:

  • Public keys are 65-byte uncompressed points (0x04, then X, then Y), not X25519’s 32-byte values.
  • The shared secret is the Weierstrass X coordinate, which differs from the X25519 shared secret by a fixed constant from the coordinate mapping.

So an X25519 implementation derives a different AES key from the same key exchange. The insidious part is that it works perfectly against itself: your own encrypt-then-decrypt tests pass, and the failure only appears at the other party, as a GCM tag that will not verify. Test against a counterparty’s implementation or published test vectors, not just a round trip.

11FHIR bundles

What gets encrypted is a FHIR R4 document bundle conforming to the NRCeS profiles for India. The first entry is a Composition whose SNOMED CT type code matches the health information type — 371530004 for an OP consultation, 440545006 for a prescription, 721981007 for a diagnostic report, 373942005 for a discharge summary.

The failures that get bundles rejected are mostly structural rather than clinical: references that point at no entry in the bundle, a Composition that is missing or not first, a missing meta.profile, a subject that resolves to nothing, or a composition code that disagrees with the declared type. Validate before encrypting, because nobody will tell you afterwards. The ABDM FHIR guide and the FHIR integration page cover the profiles in detail.

12Where integrations stall

The first successful call to ABDM is rarely where projects get stuck. These are, roughly in the order teams meet them:

  • Callback correlation. The answer arrives on a different connection, sometimes minutes later, possibly twice. Without a stored request id against every outbound call, there is no way to know what it answers. Build this first, not after the demo.
  • Callback authentication. The wrong JWKS URL makes every callback fail, and it looks like ABDM never called.
  • Bridge linkage. A facility that is in HFR but not linked to your bridge fails in ways that do not name the cause.
  • Token handling. Minting per request trips the silent 401; confusing T-tokens and X-tokens breaks ABHA profile reads.
  • Consent treated as a boolean. Sending everything, or continuing after revocation, fails functional testing and is a real privacy breach in production.
  • Encryption that only works against itself. See X25519 above.
  • Payload shape. Some rejections are a bare 400 with an empty body, identical to malformed JSON. Where a field sits — a list versus an object, hiType at the group level rather than per context — matters, and version 3 moved several of them.

None of this is exotic, but together it is most of the work, and it is common to every product that integrates. That is the case for a connector: Swastik’s ABDM API handles tokens, headers, callback verification and correlation, linking, consent enforcement, FHIR assembly and Fidelius, and lets your software call ordinary REST and receive webhooks. Whichever way you build, you can try each flow in the sandbox before committing, and the milestones guide explains how the finished integration is certified.

FAQ Common questions

Frequently asked questions

Why are ABDM APIs asynchronous?
Because most ABDM operations involve a third party — the consent manager, another provider, or the patient deciding in their app. The gateway accepts a request at once and delivers the answer later as a callback to the integrator's registered URL.
What is the bridge URL in ABDM?
The bridge URL is the public HTTPS address an integrator registers with ABDM to receive callbacks. The gateway appends each callback's path to it, so it must be reachable from the internet and verify every token ABDM signs.
What is the X-CM-ID header in ABDM?
X-CM-ID names the consent manager a request is for: sbx in the sandbox and abdm in production. It is required on gateway calls alongside REQUEST-ID, TIMESTAMP and the session token.

Next Keep reading

Related reading

GUIDE

ABDM milestones M1, M2, M3 and M4, explained

What each ABDM milestone covers — M1 ABHA, M2 HIP linking and sharing, M3 HIU consent and retrieval, and what M4 refers to — which ones your product needs, how each is tested, and what stands between the sandbox and production.

GUIDE

HIP vs HIU in ABDM: who shares, and who asks

The difference between a Health Information Provider and a Health Information User in ABDM: what each implements, how consent flows between them, what an audit checks on each side, and why most hospitals end up being both.

ABDM API

The ABDM API, as ordinary REST and webhooks

A single REST API over ABDM v3: OAuth2 client credentials, ABHA, patients and care contexts, consents, health information, HFR and HPR, with signed webhooks for every asynchronous callback and an OpenAPI reference with curl, Python and JavaScript.

ABDM SANDBOX

An ABDM sandbox you can call today

Get ABDM sandbox API credentials the moment you verify your email. Exercise ABHA, care-context linking, consent and FHIR data exchange against the NHA sandbox, with a starter facility, a live test console and a checklist for sandbox exit.

Try it against the sandbox

Sandbox API keys are issued the moment you verify your email. Create an ABHA, link a care context and watch the webhook arrive.