Developers ABDM API
The ABDM API, as ordinary REST and webhooks
ABDM v3 is a gateway, a consent manager, two registries and a callback for almost every call. This API puts one REST surface over all of it: a bearer token, JSON in and out, and a signed webhook when ABDM answers.
01 Conventions
Six rules that hold on every endpoint
Learn these once and the other ninety-odd endpoints read the same way.
- Errors
-
One envelope, a stable code
{"error": {"code", "message", "details", "request_id"}}. Thecodeis stable and safe to branch on; the message is for people. - Validation
-
422 with per-field detail
A request that is well-formed but wrong gets a
422naming each field.400is kept for input that is genuinely malformed. - Async
-
202 means accepted, never completed
ABDM answers linking, consent and data requests later, on another channel. A
202says ABDM took the request; the outcome arrives by webhook. - Retries
-
Idempotency-Key on writes
Send one on a
POST,PUTorPATCHand a retry replays the original response instead of creating a second link request. - Correlation
-
X-Request-Id, all the way through
Echoed back on the response, threaded through the ABDM calls it causes, and stamped on the webhooks that come back as
correlation_id. - Lists
-
Cursor pagination
Stable under concurrent writes, so a page boundary does not skip or repeat a record while a busy OPD is registering patients.
02 Authentication
Client credentials, then a bearer token
These are your credentials for this API. The connector’s own ABDM credentials are never exposed through it.
-
Issue a credential
From the portal, under Settings → API keys. The secret is returned once and stored hashed. Ask for a subset of your application’s scopes; asking for one it is not entitled to is refused with
403, not silently dropped. -
Exchange it for a token
POST /v1/auth/token, grant typeclient_credentials. The token is valid for an hour and names itsenvironment. -
Check it before you trust it
GET /v1/auth/whoamireturns the application, its milestone capabilities and the token’s scopes — the fastest way to explain a403 insufficient_scope. -
Contain and rotate
An IP allowlist per credential is enforced at the token endpoint and on every request. Rotation issues a successor and keeps the old key working for a 24-hour grace period while you deploy.
POST /v1/auth/token
{
"client_id": "swk_sand_…",
"client_secret": "sk_sand_…",
"grant_type": "client_credentials",
"scope": "patient:write care_context:write"
}
← 200 OK
{
"access_token": "eyJ…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "patient:write care_context:write",
"environment": "sandbox"
}
# then, on every request
Authorization: Bearer eyJ…
Idempotency-Key: link-OPD-2291
03 Resource map
The API by ABDM milestone
A credential only reaches the milestones its application is entitled to. The full list, with request and response schemas, is in the API reference.
| Area | Main endpoints | Answers arrive as |
|---|---|---|
| System | /v1/auth/token, /v1/auth/whoami, /v1/facility, /v1/facility/scan-share-qr |
Synchronous |
| M1 · ABHA | /v1/abha/enrolment/aadhaar, /v1/abha/enrolment/document, /v1/abha/login, /v1/abha/sessions/{session_id}/profile, /card, /qr |
abha.created, abha.verified |
| M2 · HIP | /v1/patients, /v1/care-contexts, /v1/care-contexts/link, /v1/link-requests/{link_request_id}, /v1/discoveries |
care_context.link_requested, care_context.linked, care_context.link_failed, consent.notified, health_information.requested, health_information.sent, health_information.failed |
| M3 · HIU | /v1/consents, /v1/consent-artefacts, /v1/health-information, /v1/records, /v1/records/{bundle_id}/content |
consent.request_acknowledged, consent.granted, consent.denied, consent.revoked, consent.expired, health_information.received, health_information.erased |
| M4 · HFR | Your hospital’s HFR record on /v1/facility; registration and bridge linkage in the portal |
Synchronous |
| M4 · HPR | /v1/practitioners (your hospital’s roster), /v1/hpr/professionals, /v1/hpr/enrolments, /v1/hpr/registrations |
Synchronous |
| Webhooks | Set up per application in the portal — signed deliveries to your URL | — |
| Audit | /v1/audit/events, /v1/audit/requests, /v1/audit/abdm-transactions, /v1/audit/trace/{correlation_id} |
— |
You send flat JSON; FHIR is built for you. A care context can carry a bundle you built, or plain fields the connector turns into an NRCeS-profiled FHIR R4 document — see FHIR integration. For what a HIP has to do with those records under consent, see HIP integration.
04 Webhooks
ABDM’s callbacks, as one signed event
ABDM calls the connector’s bridge. The connector verifies the gateway’s token, stores the callback, matches it to your request and posts one event to your endpoint.
POST https://your-hmis.example/abdm/events
X-Swastik-Event: care_context.linked
X-Swastik-Event-Id: 9c0e…
X-Swastik-Signature: t=17572…,v1=4f1a…
{
"id": "9c0e…",
"event": "care_context.linked",
"created_at": "2026-09-07T10:31:12+05:30",
"correlation_id": "5f3c9a1e",
"data": {
"patient_reference": "MRN-90881",
"care_contexts": ["OPD-2291"]
}
}
# verify before parsing
signed_payload = f"{t}.{raw_body}"
hmac_sha256(secret, signed_payload) == v1
- Signing
-
Verify, then parse
HMAC-SHA256 over the timestamp and the raw body, with the secret returned once when you register the endpoint. Compare in constant time and reject a
tolder than five minutes. - Content
-
Identifiers, never records
Payloads carry ids and status. Clinical content is fetched over the authenticated API, where the read is logged against your credential.
- Delivery
-
Retried, then retryable by you
Failed deliveries back off from seconds to hours. An exhausted one can be re-queued from the portal’s Webhooks page.
- Failure
-
Silence is not an outcome
An ABDM call that fails every retry raises
abdm.error, so a record nobody shared does not look shared.
05Idempotency, three ways
ABDM delivers at least once and hospital systems retry, so a request can arrive twice from either direction. Each direction is handled where it belongs.
- You to the connector. The first response for an
Idempotency-Keyis stored and replayed. The key is bound to the request body: reuse it with different data and you get409 idempotency_key_reuse, never a stale success. A failed request is not stored, so retrying the same key can still get through. - ABDM to the connector. Every callback is persisted before it is processed, and a duplicate of the same gateway request id is recognised rather than applied twice.
- The connector to ABDM. Retries reuse the same request id, which ABDM de-duplicates on, and a re-delivered data request reuses its transfer instead of sending the records again.
06Errors you can branch on
The status says who has to act; the code says what happened. A few worth handling explicitly:
401 token_expired— exchange the credential again. Tokens last an hour.403 insufficient_scope— the token lacks a scope;/v1/auth/whoamishows which it has.403 ip_not_allowed— the request came from outside the credential’s allowlist.403 production_access_not_granted— a production call from an organization not yet approved.422with anabdm_code — ABDM rejected the request itself, and its own error code is passed through, because that is what separates an expired OTP from a malformed one.429 abdm_rate_limited— ABDM is throttling; retry later rather than immediately.503— ABDM could not be reached, the request was not delivered, and the identical request is safe to retry.
07When you cannot receive webhooks
Webhooks are the normal path, but the state behind every event is also
readable. An on-premise system that cannot expose an HTTPS endpoint can
poll the request it made: GET /v1/link-requests/{link_request_id}
for a link, GET /v1/consents/{consent_request_id} for a
consent request, GET /v1/health-information/{hi_request_id}
for a data request.
POST /v1/consents/{consent_request_id}/refresh re-asks ABDM
itself, and is for a notification that appears lost. Calling it on a
schedule spends a rate limit that every tenant of the connector
shares.
08Audit and the end-to-end trace
Four read endpoints answer “what happened” without a
support ticket. /v1/audit/requests lists your API calls
with status and duration. /v1/audit/abdm-transactions
lists the calls the connector made to ABDM for you, with the
abdm_request_id ABDM support will ask for.
/v1/audit/events records privacy-significant changes.
/v1/audit/trace/{correlation_id} puts them together: your
call, the ABDM calls it caused and the audit events that followed,
under one id. It is the shortest route from “this didn’t
work” to where it stopped. For the flow those ids follow, read
how ABDM integration
works.
Read the spec, then call it
Sandbox keys are issued on email verification. The reference has curl, Python and JavaScript for every endpoint.
FAQ Common questions