feat(abdm): configure official ABDM toolkit skills and achieve 100% NRCES 6.5.0 FHIR conformance

This commit is contained in:
DevSoftTech Server 2026-10-06 12:25:48 +05:30
parent e553215ea1
commit 24819cb75c
121 changed files with 32735 additions and 35 deletions

13
.agents/mcp_config.json Normal file
View File

@ -0,0 +1,13 @@
{
"mcpServers": {
"abdm-docs": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://docs.abdm.gov.in/mcp"
],
"url": "https://docs.abdm.gov.in/mcp"
}
}
}

View File

@ -0,0 +1,66 @@
---
name: abdm-fhir
description: Use when producing or checking FHIR for ABDM: building NRCES compliant document bundle generation into a codebase, or auditing the bundles an existing FHIR store already emits. Covers the resource profiles ABDM requires, the Composition rules, and the validator to check against.
type: skill
domain: fhir
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
- fhir-compliance-agent
requires:
- abdm-docs-mcp
produces:
- nrces-document-bundle
can_execute: true
can_orchestrate: false
---
# ABDM FHIR
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-fhir/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
## What this skill covers
- **Design.** What a generator gets wrong before it reaches a profile table. [references/design.md](references/design.md)
- **Generate.** Build NRCES compliant bundle generation into a codebase. [references/generate.md](references/generate.md)
- **Audit.** Check an existing FHIR store's output against the same profiles. [references/audit.md](references/audit.md)
Open one when the work calls for it. This file is the map, not the material.
## Try asking
- "Add ABDM compliant FHIR bundle generation to this codebase"
- "Audit the FHIR bundles this codebase already emits"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- A bundle that validates is not a bundle ABDM accepts. The NRCES profiles are the floor, and the milestone the bundle travels under adds its own rules on top.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.

View File

@ -0,0 +1,91 @@
# fhir-audit
Audit an existing FHIR store's output against ABDM's NRCES profiles,
and fix the generation pipeline where it falls short. The store stays
authoritative: this skill patches what emits from it, it does not
replace it.
## 1. Collect representative output
From the integrator, get one real (or realistic test) document bundle
per record type their store produces, saved as local `.json` files.
Do not synthesize bundles yourself for this step; the point of the
audit is what the store actually emits, not what a correctly built
bundle would look like.
## 2. Tier 1 pass
Run `validate` with `kind: fhir` on each bundle, passing its `record_type`. Collect
every finding across every bundle into one gap report, grouped by
finding (the shape of the defect, for example "`Composition.author`
missing" or "`Composition.meta.profile` does not include ..."), not by
which bundle it came from. The same defect usually repeats across every
bundle the store produces, because it comes from one place in the
pipeline; grouping by bundle would make the same root cause look like
many separate problems.
## 3. Interpret with the knowledge surface
For each finding, use its Ref to read what NRCES actually demands and
why ABDM checks it. Ref names the NRCES profile the rule came from
(for example `OPConsultRecord` or `DocumentBundle`), not an atom id
directly; map it to the record type's mapping atom via `get`:
`shared.fhir.document-bundles` for envelope-level findings (Ref
`DocumentBundle`), or the type's mapping atom
(`shared.fhir.map-opconsultation`, `shared.fhir.map-prescription`,
`shared.fhir.map-diagnosticreport`, `shared.fhir.map-dischargesummary`,
`shared.fhir.map-immunizationrecord`,
`shared.fhir.map-healthdocumentrecord`, or
`shared.fhir.map-wellnessrecord`) for a Composition-element or
section-level finding. Four record types (Prescription,
DiagnosticReport, HealthDocumentRecord, ImmunizationRecord) have no
NRCES section slices: do not expect or ask for section-by-section
findings on these four, only Composition-element and entry-level ones.
Sort each finding into one of two buckets:
- **Generation-time fix.** The store already captures the data; the
pipeline just emits the wrong shape (wrong or missing
`meta.profile`, a malformed reference, a missing `Bundle.identifier`
field). This is fixable in code, in this session.
- **Mapping gap.** The store never captures the data a required
element needs at all (no encounter record to populate
`Composition.encounter`, no coded allergy list to populate an
`AllergyIntolerance` entry). This is not a code fix; it is a
conversation with the integrator about what their store needs to
start capturing, and it belongs in the gap report as an open item,
not a silent workaround.
## 4. Patch, do not replace
For every generation-time fix, amend the integrator's existing
generation pipeline directly, or add a thin adapter that post-processes
the store's output into the compliant shape if the pipeline itself is
not reasonably editable. Either way the store keeps producing its own
output; this skill's job is the layer between that output and what
ABDM requires, not a replacement generator.
## 5. Tier 2 pass
Follow `shared.fhir.hl7-validator-recipe` against the patched output
for each record type: download the pinned `validator_cli.jar`, run it
with `-ig ndhm.in#6.5.0` (the recipe atom is the authoritative source
for the pinned version, defer to it if this ever looks stale), and
iterate the same way as tier 1, fix, re-run, until the report lists no
errors. A warning is not the same as a pass; read each one and decide
deliberately whether it is acceptable.
## 6. Done means
The gap report shows every original finding either resolved (with the
fix that resolved it) or explicitly accepted by the integrator (mapping
gaps they have chosen to defer, named as such, not silently dropped).
Tier 1 and tier 2 are both clean against every collected bundle, and
the exact commands to re-run both checks are recorded in the
integrator's repo (a make target or script) so the checks outlive this
session.
Never invent clinical codes: where the integrator's data lacks a coded
value, surface the gap to the human rather than fabricating a SNOMED
code (tier 1 will not catch a wrong code; tier 2 may not either without
a terminology server).

View File

@ -0,0 +1,115 @@
# fhir-generate
Scaffold FHIR document bundle generation for one or more ABDM record
types, directly into the integrator's own codebase, and drive it to a
clean bill of health against both validation tiers.
## 1. Establish scope
Call `search` with `kind: fhir_profile` to get the full list of ABDM record types
(the seven `hiType` values: OPConsultation, Prescription,
DiagnosticReport, DischargeSummary, ImmunizationRecord,
HealthDocumentRecord, WellnessRecord). Confirm with the integrator
which of these seven their system must produce; do not build all seven
speculatively if only one or two are needed.
Identify the language and the repository (or the path within a
monorepo) where the generation module will live. If the integrator
already has a FHIR-adjacent module (a `fhir/`, `hie-cm/`, or similar
directory), that is where this work belongs.
## 2. Learn the target shape
For each in-scope record type:
- Call `get` with `fhir:<that type>`, and once with `fhir:DocumentBundle`
(the shared envelope profile every record type rides on top of).
- Call `get` with `fhir-example:<that type>`, to see a concrete, valid
bundle.
- Read `shared.fhir.document-bundles` via `get` for the envelope
rules that apply to every record type (`Bundle.type`, `Bundle.meta`,
`Bundle.identifier`, `Bundle.timestamp`).
- Read that type's mapping atom via `get`
(`shared.fhir.map-opconsultation`, `shared.fhir.map-prescription`,
`shared.fhir.map-diagnosticreport`, `shared.fhir.map-dischargesummary`,
`shared.fhir.map-immunizationrecord`,
`shared.fhir.map-healthdocumentrecord`, or
`shared.fhir.map-wellnessrecord`) for the Composition's required
elements and, where NRCES defines them, its section slices.
Four record types (Prescription, DiagnosticReport, HealthDocumentRecord,
ImmunizationRecord) have no NRCES section slices: their Composition
carries one unnamed, required section whose `entry` array is what is
actually constrained. Do not scaffold section-by-section guidance for
these four; scaffold the required entry resource types their mapping
atom names instead. OPConsultation, DischargeSummary and WellnessRecord
do have named section slices; use the mapping atom's section table for
those.
## 3. Scaffold in the integrator's stack
For each in-scope record type, write a builder function that:
- Takes the integrator's own domain data as input (their patient,
encounter, order, or report model, whatever they call it).
- Produces a document bundle matching the digest returned by
`get` with `fhir:<type>`: the correct `meta.profile` URL on the
Composition, every required top-level element the digest lists, and
the section shape the mapping atom describes for that type.
- Resolves every reference internally: any resource a section entry or
the Composition points at must be added to the bundle as its own
entry, not left dangling.
Then write one bundle-level wrapper, shared across all in-scope
builders, that applies the `DocumentBundle` envelope constraints:
`Bundle.type` fixed to `"document"`, `Bundle.meta` and
`Bundle.meta.versionId` set, `Bundle.identifier` (`.system` and
`.value`) set, `Bundle.timestamp` set. The Composition must be the
bundle's first entry.
Follow the repository's existing conventions for module layout, error
handling, and naming. If the integrator's stack already has a FHIR
library (a typed resource model, a bundle builder), build on it rather
than duplicating it. If it has none, plain JSON construction (maps or
structs marshaled to JSON) is fine; do not introduce a new FHIR library
dependency just for this.
## 4. Iterate against tier 1
Run each builder's output bundle through `validate` with `kind: fhir`, passing the
bundle and its `record_type`. For every finding returned:
- Read the finding's Fix text.
- Apply exactly that fix to the builder (not a workaround in the test
data).
- Re-run `validate` with `kind: fhir`.
Repeat until `validate` returns no findings for that record type,
then move to the next in-scope type.
## 5. Finish with tier 2
Tier 1 is structural and fast; it does not check nested cardinalities,
terminology bindings, or section slice discriminators. Follow
`shared.fhir.hl7-validator-recipe` end to end, in the integrator's own
environment: download the pinned `validator_cli.jar`, run it against
each builder's output bundle with `-ig ndhm.in#6.5.0` (the recipe atom
is the authoritative source for the pinned version, defer to it if
this ever looks stale), and resolve every error the report lists. A
warning is not an error; read each one and decide deliberately whether
it is acceptable, rather than treating a warning-only run as clean.
## 6. Done means
For every in-scope record type: tier 1 (`validate`, `kind: fhir`) returns no
findings, and tier 2 (the official HL7 validator) exits 0 with no
errors, against IG 6.5.0. Record the exact commands to re-run both
checks (a make target, an npm script, or a shell script committed to
the integrator's repo) so the checks outlive this session and the next
person who touches this module can re-verify without re-deriving the
commands.
Never invent clinical codes: where the integrator's data lacks a coded
value, surface the gap to the human rather than fabricating a SNOMED
code (tier 1 will not catch a wrong code; tier 2 may not either without
a terminology server).

View File

@ -0,0 +1,79 @@
---
name: abdm-gateway
description: Use when building, debugging or testing the ABDM gateway: the gateway session and bridge registry.
type: skill
domain: gateway
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- sandbox-client-credentials
produces:
- gateway-session-token
- bridge-url
can_execute: true
can_orchestrate: false
---
# ABDM Gateway, sessions and the bridge registry
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-gateway/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with Gateway
- Bridge
- Session
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Get a gateway access token and register this bridge"
- "What has to be in place before my first Gateway call?"
- "My Gateway call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 4 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/gateway
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug Gateway, sessions and the bridge registry
The codes this module can return, with the message and the operation that returns each.
## Errors
The Gateway, sessions and the bridge registry specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,37 @@
# Integrate Gateway, sessions and the bridge registry
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
4 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,208 @@
# HIE-CM gateway build
Scaffolds an ABDM gateway integration one journey at a time. It covers the gateway session and bridge registry.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Bridge (`gateway-abdm-gateway`)
**Act: the calls in this journey, in order**
#### 1. Fetch the service ids registered against a bridge (`gateway_get_gateway_v3_bridge_services`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-services \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
#### 2. Fetch the details of a service ID (`gateway_get_gateway_v3_bridge_service_serviceid_service_id`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
#### 3. Update the bridge URL (`gateway_patch_gateway_v3_bridge_url`)
```bash
curl --request PATCH \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge/url \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"url": "<YOUR_CALLBACK_URL>"
}'
```
**Exit condition (Observe until this is true)**
A 202 response. The specification gives no body for it, so read what comes back.
### Session (`gateway-abdm-sessions`)
**Act: the calls in this journey, in order**
#### 1. Generate access token (`gateway_post_gateway_v3_sessions`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/sessions \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"clientId": "SBX_0000",
"clientSecret": "0******-***-***-***-a****",
"grantType": "client_credentials"
}'
```
**Exit condition (Observe until this is true)**
A 202 whose body matches:
```json
{
"accessToken": "<TOKEN>",
"expiresIn": 1200,
"refreshExpiresIn": 1800,
"refreshToken": "<TOKEN>",
"tokenType": "bearer"
}
```
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/gateway

View File

@ -0,0 +1,102 @@
---
name: abdm-m1
description: Use when building, debugging or testing ABDM Milestone 1: creating an ABHA number or address, ABHA login, profile management, or the gateway session token. Carries the endpoints, the required headers, the two token rule, the encryption rule and the error codes its specification's examples return. Also carries the scaffolding loop that builds it flow by flow and the loop from a failed call to a named fix, in references/.
type: skill
domain: m1
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
produces:
- abha-number
- abha-address
- user-token
- abha-profile
can_execute: true
can_orchestrate: false
---
# ABDM M1, create and verify ABHA
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m1/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M1
- Create an ABHA for somebody who has none: by Aadhaar OTP, by demographic authentication, by face or fingerprint, or under a parent for a child.
- Log in somebody who already has one, by Aadhaar, mobile, ABHA number or ABHA address.
- Read their profile, which carries the whole registration form: names, date of birth, gender, mobile, address with its codes, and a photograph.
- Find an ABHA somebody has forgotten, and pick the right account when one mobile holds several.
- Show the ABHA card and QR code, and take a profile a patient shares by QR at your counter.
- Update a profile and change a mobile.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Add ABHA creation by Aadhaar OTP to this codebase"
- "What has to be in place before my first M1 call?"
- "My M1 call returned ABDM-1017. What is wrong, and how do I fix it?"
- "Walk me through the M1 test cases before go-live"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 123 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 17 error codes from the specification's examples. [references/debug.md](references/debug.md)
- **Test.** The functional test cases, one loop each, with the evidence the sandbox can vouch for and the manifest that replaces a screenshot report. [references/test.md](references/test.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- ABDM publishes operations, not a user experience. The journey is the integrator's to design, so offer the shape below as a suggestion and build what they ask for instead when they have a view of their own counter.
- The ABHA step comes before the registration form and fills it. A verified profile carries the whole form, so a journey that registers the patient first and offers ABHA afterwards has already spent the keystrokes it existed to save.
- Two ways the profile reaches the desk. The patient scans a QR and consents in their own app, and ABDM posts the profile to your callback, so nobody types or asks anything. Or the desk runs the identifier journey. The filled form is the destination either way.
- Decide deliberately whether a journey can complete without an ABHA. A record keyed by the facility's own number does not need one, and a journey that cannot finish without one blocks care for anyone who has none.
- An identifier and an auth method are two different questions, and listing them together is what makes M1 look like five choices. Ask which identifier first, then offer auth methods underneath it, and only where there is more than one.
- Every identifier starts on the login path, Aadhaar included. A verified login already returns the accounts, and only ABDM-1114 on a refusal means nobody holds one. Create on any other answer and the patient ends up with two numbers, which nothing merges.
- Get an access token first, from the gateway session endpoint. Every other call needs it in `Authorization: Bearer <token>`.
- Two tokens exist and they are not interchangeable. The gateway access token goes in `Authorization`. The user token from an enrolment or a login goes in `X-token`. Profile endpoints need both.
- Sensitive fields travel encrypted. Aadhaar numbers, mobile numbers, email addresses, OTP values and passwords are encrypted before they go in the body, then base64 encoded.
- The certificate response tells you the padding. `GET /v3/profile/public/certificate` returns `{"publicKey", "encryptionAlgorithm"}`. Read that field and translate it for your language. Do not hard code a padding: a constant is right until it is not, and the failure then looks like a bad value rather than a stale constant.
- One path serves several jobs. The `scope` array in the body picks which one, so read it before assuming an endpoint does one thing.
- The accounts array on a login verification already carries ABHANumber, preferredAbhaAddress, name, gender, dob, profilePhoto and kycVerified, so a registration form can fill the moment the OTP verifies and before any profile call.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m1
- The flows as diagrams: /docs/hiecm/v3/milestones/m1
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,139 @@
# HIE-CM m1 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### 404
**Specification example:** HTTP 404, `Runtime Error`, on `m1_post_v3_profile_login_verify_find_abha_face`.
**Exit condition: the original call now succeeds.**
### 900900
**Specification example:** HTTP 500, `Unclassified Authentication Failure`, on `m1_post_v3_enrollment_auth_byabdm_mobile_verify_create_ab_091285`.
**Exit condition: the original call now succeeds.**
### 900901
**Specification example:** HTTP 401, `Invalid Credentials`, on `m1_post_v3_enrollment_request_otp_aadhaar_otp`.
**Exit condition: the original call now succeeds.**
### 900902
**Specification example:** HTTP 401, `Missing Credentials`, on `m1_post_v3_profile_benefit_search_xmluid`.
**Exit condition: the original call now succeeds.**
### ABDM-1017
**Specification example:** HTTP 401, `Invalid Transaction Id`, on `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp`.
**Exit condition: the original call now succeeds.**
### ABDM-1021
**Specification example:** HTTP 401, `Lack of required priviledges`, on `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1094
**Specification example:** HTTP 401, `X-token expired`, on `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1114
**Specification example:** HTTP 404, `User not found`, on `m1_patch_v3_profile_account_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1124
**Specification example:** HTTP 422, `The mobile number provided by you is already linked to 6 ABHA Numbers. Please provide a different Mobile Number.`, on `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d`.
**Exit condition: the original call now succeeds.**
### ABDM-1138
**Specification example:** HTTP 400, `The benefit record has already been de-linked`, on `m1_post_v3_profile_benefit_linkanddelink_x_token`.
**Exit condition: the original call now succeeds.**
### ABDM-1140
**Specification example:** HTTP 400, `The benefit record has already been linked`, on `m1_post_v3_profile_benefit_linkanddelink_x_token`.
**Exit condition: the original call now succeeds.**
### ABDM-1157
**Specification example:** HTTP 422, `Child ABHA’s account limit has been exceeded for the requested Abha ID number ‘<ABHA_NUMBER>`, on `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1160
**Specification example:** HTTP 422, `Non KYC CHILD ABHA is allowed to update their profile only once`, on `m1_patch_v3_profile_account_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1204
**Specification example:** HTTP 422, `UIDAI Error code : 400 : Invalid Aadhaar OTP value.`, on `m1_post_v3_enrollment_enrol_byaadhaar_otp`.
**Exit condition: the original call now succeeds.**
### ABDM-1207
**Specification example:** HTTP 422, `The information you provided does not match the details on record with Aadhaar. Please verify and provide accurate information.`, on `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d`.
**Exit condition: the original call now succeeds.**
### ABDM-1211
**Specification example:** HTTP 400, `User not found.`, on `m1_post_v3_phr_web_login_abha_search_abha_address_login_m_27a20f`.
**Exit condition: the original call now succeeds.**
### ABDM-1224
**Specification example:** HTTP 401, `Login via Biometric is not allowed.`, on `m1_post_v3_profile_login_request_otp_find_abha_face`.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/m1
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `404` | 404 | Runtime Error | `m1_post_v3_profile_login_verify_find_abha_face` |
| `900900` | 500 | Unclassified Authentication Failure | `m1_post_v3_enrollment_auth_byabdm_mobile_verify_create_ab_091285` |
| `900901` | 401 | Invalid Credentials | `m1_post_v3_enrollment_request_otp_aadhaar_otp` |
| `900902` | 401 | Missing Credentials | `m1_post_v3_profile_benefit_search_xmluid` |
| `ABDM-1017` | 401 | Invalid Transaction Id | `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp` |
| `ABDM-1021` | 401 | Lack of required priviledges | `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha` |
| `ABDM-1094` | 401 | X-token expired | `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha` |
| `ABDM-1114` | 404 | User not found | `m1_patch_v3_profile_account_child_child_abha` |
| `ABDM-1124` | 422 | The mobile number provided by you is already linked to 6 ABHA Numbers. Please provide a different Mobile Numb… | `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d` |
| `ABDM-1138` | 400 | The benefit record has already been de-linked | `m1_post_v3_profile_benefit_linkanddelink_x_token` |
| `ABDM-1140` | 400 | The benefit record has already been linked | `m1_post_v3_profile_benefit_linkanddelink_x_token` |
| `ABDM-1157` | 422 | Child ABHA’s account limit has been exceeded for the requested Abha ID number ‘<ABHA_NUMBER> | `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha` |
| `ABDM-1160` | 422 | Non KYC CHILD ABHA is allowed to update their profile only once | `m1_patch_v3_profile_account_child_child_abha` |
| `ABDM-1204` | 422 | UIDAI Error code : 400 : Invalid Aadhaar OTP value. | `m1_post_v3_enrollment_enrol_byaadhaar_otp` |
| `ABDM-1207` | 422 | The information you provided does not match the details on record with Aadhaar. Please verify and provide acc… | `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d` |
| `ABDM-1211` | 400 | User not found. | `m1_post_v3_phr_web_login_abha_search_abha_address_login_m_27a20f` |
| `ABDM-1224` | 401 | Login via Biometric is not allowed. | `m1_post_v3_profile_login_request_otp_find_abha_face` |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
# Integrate M1, create and verify ABHA
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://abhasbx.abdm.gov.in` ABHA service, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
123 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/abha/api/v3.1/profile/login/verify` | Login via Face Auth (QR) - verify |
| `POST` | `/abha/api/v3.1/profile/login/verify` | Login via Biometric (Fingerprint) - v3.1 single step |
| `POST` | `/abha/api/v3.1/profile/login/verify` | Login via Biometric (Iris) - v3.1 single step |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/auth/init` | Face auth - generate transaction ID (init) |
| `POST` | `/abha/api/v3/enrollment/enrol/auth/init` | Face auth - generate transaction ID (init) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create Child ABHA |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create Child ABHA |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Demographic authentication (Demo Auth) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Demographic authentication (Demo Auth) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Aadhaar face authentication |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Aadhaar fingerprint (bio) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Aadhaar IRIS |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/enrollment/enrol/capturePID` | Face auth - capture PID / track status |
| `POST` | `/abha/api/v3/enrollment/enrol/capturePID` | Face auth - capture PID / track status |
| `POST` | `/abha/api/v3/enrollment/enrol/capturePID` | Face auth - capture PID / track status |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/profile/children` | Get Child ABHA list of the parent |
| `GET` | `/abha/api/v3/enrollment/profile/children` | Get Child ABHA list of the parent |
| `POST` | `/abha/api/v3/enrollment/request/otp` | Send Aadhaar OTP for ABHA enrolment |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via Aadhaar OTP - send OTP |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via Fingerprint - send authentication request |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via IRIS - send authentication request |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via Mobile OTP - send OTP |
| `POST` | `/abha/api/v3/phr/web/login/abha/search` | Search ABHA address (auth methods) |
| `POST` | `/abha/api/v3/phr/web/login/abha/search` | Search ABHA address (auth methods) |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via Aadhaar OTP - verify |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via Fingerprint - verify |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via IRIS - verify |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via Mobile OTP - verify |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/profile/account` | Get ABHA profile |
| `PATCH` | `/abha/api/v3/profile/account` | Update Child ABHA profile |
| `PATCH` | `/abha/api/v3/profile/account` | Update Child ABHA profile |
| `GET` | `/abha/api/v3/profile/account/abha-card` | Retrieve ABHA card image |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `GET` | `/abha/api/v3/profile/account/download-abha-card` | Download ABHA card |
| `GET` | `/abha/api/v3/profile/account/qrCode` | Get ABHA QR code |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Child ABHA KYC - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Child ABHA KYC - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Re-KYC - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Update mobile - send OTP |
| `GET` | `/abha/api/v3/profile/account/request/token` | Refresh user token |
| `POST` | `/abha/api/v3/profile/account/verify` | Child ABHA KYC - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/verify` | Child ABHA KYC - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/verify` | Re-KYC - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/verify` | Update mobile - verify OTP |
| `GET` | `/abha/api/v3/profile/benefit/abha/{abhanumber}` | Get benefits linked to an ABHA number |
| `POST` | `/abha/api/v3/profile/benefit/linkAndDelink` | Link / De-link benefit using ABHA number |
| `POST` | `/abha/api/v3/profile/benefit/linkAndDelink` | Link / De-link benefit using X-token |
| `POST` | `/abha/api/v3/profile/benefit/linkAndDelink` | Link / De-link benefit using xmlUid |
| `POST` | `/abha/api/v3/profile/benefit/search` | Benefit search by ABHA number |
| `POST` | `/abha/api/v3/profile/benefit/search` | Benefit search by xmlUid |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Aadhaar number - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via ABHA number - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via ABHA number - send ABHA (mobile) OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Biometric (Fingerprint) - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Biometric (Iris) - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Aadhaar OTP - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Face - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Fingerprint - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via IRIS - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Mobile - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Forgot ABHA via Aadhaar - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Forgot ABHA via Mobile - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Mobile number - send OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Aadhaar number - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via ABHA number - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via ABHA number - verify ABHA (mobile) OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Biometric (Fingerprint) - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Biometric (Iris) - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Aadhaar OTP - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Face - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Fingerprint - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via IRIS - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Mobile - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Forgot ABHA via Aadhaar - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Forgot ABHA via Mobile - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Mobile number - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify/user` | Login via Mobile number - verify user (select ABHA) |
| `GET` | `/abha/api/v3/profile/public/certificate` | Get public certificate (RSA encryption key) |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for every request. |
| `TIMESTAMP` | Current UTC timestamp in ISO-8601 format. |
| `BENEFIT_NAME` | **Applicable for user who is enrolling via Benefit Program.** |
| `X-token` | **Applicable for child abha creation. X-token of Parent user, user can get X-token after login to the system** |
| `TRANSACTION_ID` | Transaction ID from the ABHA creation response. |
| `Content-Type` | `application/json`. |
| `R-token` | Refresh token (`Bearer <refreshToken>`) received at login. |
| `T-token` | Short-lived token (`Bearer <token>`) returned by *Login via Mobile number - verify OTP*. |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://abhasbx.abdm.gov.in/abha/api/v3.1/profile/login/verify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'Content-Type: application/json' \
--data '{
"scope": [
"abha-login",
"aadhaar-face-login-verify"
],
"authData": {
"authMethods": [
"face_auth"
],
"face_login": {
"txnId": "8220299c-40ad-40b6-bc52-13a7d46a69d0",
"aadhaar": "{{encrypted aadhaar number}}"
}
}
}'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,401 @@
# HIE-CM m1 test
Walks the ABDM M1 functional test cases, ABHA creation and verification, against the system you built and the sandbox. Each case ends in one of four recorded outcomes, never in an opinion.
## What this skill does
- Runs every M1 test case in the ABHA creation and verification set against your system and the sandbox, one case at a time
- Records, for each case, the sandbox response and REQUEST-ID that prove it, or the screen that has to be attested, or the human step it stopped at
- Writes a manifest of case ids to request ids at the end, which is the evidence a reviewer checks against the gateway rather than a folder of screenshots
## What it needs from you
| Need | Where it comes from |
|---|---|
| Sandbox client id and secret | The ABDM sandbox portal, under your registered application |
| Your system running against the sandbox hosts | Your deployment or a local build. The skill drives your system, not the sandbox directly |
| A way to trigger each journey in your system | A screen, an API or a script. Say which at the start |
| A person with an Aadhaar linked mobile, for OTP cases | Present at the desk for the run. Every OTP is a human step and the skill stops for it |
| A fingerprint or face device, for the biometric cases | Only if you claim those cases. Without one they are recorded as not run |
| Whether you are a government or a private integrator | The set marks some cases mandatory for one and optional for the other |
## Say one of these
- "Walk the M1 test cases against my sandbox and log each one."
- "Run only the ABHA verification cases, VRFY_ABHA_101 to 405."
- "Re-run CRT_ABHA_112, the address suggestion case failed yesterday."
- "Write a terminal script that runs the M1 test cases and asks me for the Aadhaar number and each OTP."
## What happens first
One question: which cases. All of them, one function group, or a list of ids. Then the preconditions for the first case are checked before anything is sent. Nothing else is asked until a case needs a human.
## How this skill runs
Every case is an observe, orient, decide, act loop. Observe the live state: the last response, the last REQUEST-ID, what your screen shows. Orient against the case below. Decide the cheapest action that produces a new observation. Act, paste the raw result, return to observe. A case is done only when its exit condition is observed, never because the step should have worked.
Loop limit: 3 passes per test case. Hitting the limit is an escalation: state what was observed with request ids, what was tried, name one section of this skill or one operation page to read, and ask one question.
### The four outcomes
| Outcome | Record it when | Never when |
|---|---|---|
| `passed` | The exit condition is observed and the observation is pasted verbatim | The response was not pasted. A pass with no output is a fail |
| `failed` | Three passes and the exit condition was not observed, or the sandbox returned the refusal the case expects you to handle and your system did not handle it | A human step was missing. That is `needs-human` |
| `needs-human` | The case reached an OTP, a biometric scan, an operator's document check, or a person who was not there | Ever as a substitute for a fail you can see |
| `not-run` | The case does not apply to your entity, you do not claim the optional capability, or ABDM publishes no operation for it | To skip a mandatory case that applies to you |
### The three kinds of evidence
Every case names one of these. It decides what the manifest carries.
- **sandbox.** A call the gateway sees. Evidence is the HTTP status, the body and the REQUEST-ID, pasted. The exit condition is a literal in the body. A reviewer can check this against the gateway's own record, which is why it outranks the other two.
- **screen.** A rule about what your system shows or stores, which no call proves. Evidence is one screenshot or a short recording and the design rule it satisfies. A screen case is attested, not observed, and the manifest marks it so.
- **human.** A step only a person can do. Evidence is the observation just before it and the outcome `needs-human` when nobody was there.
### Rules that hold for every case
- Search before create. Every identifier goes to the login path first, and only ABDM-1114 on the verify says nobody holds an account. Creating on any other answer gives the patient two numbers, which nothing merges. `hiecm.concept.m1-avoiding-duplicate-abha`
- Never ask twice. A value the transaction already holds, the Aadhaar number, the mobile, the txnId, is carried, not re-entered. `hiecm.concept.m1-never-ask-twice`
- Two tokens. The gateway access token goes in `Authorization`. The user token an enrolment or a login returns goes in `X-token`. Profile calls need both.
- Fresh `REQUEST-ID` on every call, logged before sending. A reused id is refused, and after a failure the id is the only handle on the call.
- Paste the response. A step with no pasted output is not done.
- Sensitive values travel encrypted: Aadhaar, mobile, email, OTP. Read the algorithm from the certificate call rather than hard coding a padding.
- One question when you escalate. The person is being interrupted.
### The run record
Append one block per loop pass. It is the raw material for the manifest.
```text
case: CRT_ABHA_101 pass: 1 of 3 at: 2026-09-29T10:14:02Z
observed: POST /abha/api/v3/enrollment/enrol/byAadhaar REQUEST-ID 6f1c... HTTP 200
body: {"message":"Account created successfully","txnId":"...","tokens":{"token":"<X-token>",...},"ABHAProfile":{"ABHANumber":"91-...",...}}
matched: exit condition for CRT_ABHA_101, step 2
next: CRT_ABHA_113
```
### When the run is a script
Asked for a test script, a test command or automated tests, build one interactive terminal runner that walks the cases below. It does the typing. Every rule above still holds.
| Rule | Why |
|---|---|
| Name every check by its case id from this skill, such as `CRT_ABHA_101`. Never invent a numbering such as `TC01` | The functional testing report quotes these ids. A check with no case id answers nothing a reviewer asks |
| At each `human` step, stop and prompt in the terminal for the value: Aadhaar number, mobile, OTP, the chosen address. Read it from stdin, encrypt it, send it | Every M1 success path passes through an OTP. A runner that never asks never reaches one |
| Never write a typed value to disk, a log or the manifest, and never hard code one | An Aadhaar number and an OTP belong to the person, not the run |
| Run each `sandbox` case's success path and the refusal its exit condition names, as separate checks | The case passes only when both are observed |
| Compare the status and the body literal the exit condition names, such as `200` with `tokens.token` present, or `422` with `error.code` `ABDM-1204` | A status alone never passes a check |
| A `4xx` passes only where the case's exit condition names that refusal | A `400` on a success path step is `failed` |
| A blank answer at a prompt records the case `needs-human` and moves to the next case | Nobody present is neither a pass nor a fail |
| A `screen` case prints its check, then asks `passed? y/n` and the evidence file name | No call proves a screen, so the case is attested |
| At the end, print one line per case id with its outcome and request ids, then write the manifest in the last section | The person reads outcomes by case id, not by script step |
A run reads like this. The person's values are typed at the prompts and never echoed back.
```text
CRT_ABHA_105 POST /v3/enrollment/request/otp REQUEST-ID 3b9e... HTTP 200 txnId present matched
Enter the OTP sent to the mobile ending 1234, or leave blank if nobody is here:
CRT_ABHA_107 wrong OTP REQUEST-ID 7c02... HTTP 422 error.code ABDM-1204 matched
CRT_ABHA_107 right OTP REQUEST-ID 9a41... HTTP 200 tokens.token present matched
CRT_ABHA_107 passed
```
Nudge: calls with a bad `scope`, a bad `loginHint` or a made up `txnId` test your client, not a case. Run them first if you want them, label them `preflight`, and keep them out of the case counts.
## Test cases
The ids, the mandatory marks and the wording of each check are ABDM's, from the M1 ABHA creation and verification set. The sections are grouped as the set groups them. Where the set names an operation, the operation id here is the one in `references/integrate.md`, which carries the host, the headers and a full request.
### CRT_ABHA_101 to 115: create an ABHA by Aadhaar OTP
Applies to every integrator. Mandatory unless marked. This is the group every other creation group repeats with a different authentication step, so it is written in full and the others state only what differs.
Loop limit: 3 passes per test case.
**Preconditions.** A gateway access token less than 30 minutes old. A person present with an Aadhaar linked mobile. Their Aadhaar number searched first on the login path and not found, so the run creates rather than duplicates.
#### CRT_ABHA_101, the option exists. `sandbox`
Your system offers ABHA creation by Aadhaar OTP, and the journey completes. Drive it end to end through your screen or API.
Calls, in order: `m1_post_v3_enrollment_request_otp_aadhaar_otp`, `m1_post_v3_enrollment_enrol_byaadhaar_otp`, the optional mobile and email verifications, `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp`, `m1_post_v3_enrollment_enrol_abha_address_create_abha_aadhaar_otp`, then `m1_get_v3_profile_account` and `m1_get_v3_profile_account_abha_card`.
Exit condition: three literals, all pasted.
1. `m1_post_v3_enrollment_enrol_byaadhaar_otp` returns 200 with `tokens.token` present and `ABHAProfile.ABHANumber` present.
2. `m1_post_v3_enrollment_enrol_abha_address_create_abha_aadhaar_otp` returns 200 with `healthIdNumber` and `preferredAbhaAddress`.
3. `m1_get_v3_profile_account`, sent with that `X-token`, returns 200 with the same `ABHANumber`.
Nudge: read `message` on step 1. `Account created successfully` is a new account. `This account already exist` is a 200 too, and it means the search-first rule was skipped or the sandbox person already holds one. Record which, because the second is a screen failure on CRT_ABHA_101 even though the API passed.
#### CRT_ABHA_102, consent is shown and recorded. `screen`
Your system shows the published ABDM consent text before the Aadhaar number is asked for, and stores that the person agreed, with when. Evidence: the consent screen, and the stored record for this run. `hiecm.concept.m1-screen-contract`
#### CRT_ABHA_103, consent in another language. `screen`, optional
The same consent offered in at least one language other than English. Evidence: the screen in that language. Record `not-run` if you do not claim it.
#### CRT_ABHA_104, an invalid Aadhaar number is refused before any call. `screen`
Enter an 11 digit and a 13 digit value. Your system refuses each with a message that says the number is not valid, and sends nothing. Evidence: the refusal on screen and an empty call log for that attempt. Your own check has to be the strict one here; the sandbox's refusal for a bad number is not the one this case is about. `hiecm.concept.m1-honest-screen-states`
#### CRT_ABHA_105, the Aadhaar OTP is requested and can be entered. `sandbox`, then `human`
Exit condition: `m1_post_v3_enrollment_request_otp_aadhaar_otp` returns 200 with a `txnId` and a `message` that names the masked mobile. Paste it. Then stop: the OTP arrives on the person's phone and only they enter it. If they are not there, record `needs-human` and the txnId, and do not mark the case passed or failed.
Screen check alongside: the prompt tells the person the OTP went to the mobile linked with Aadhaar and shows the last digits, which the `message` carries.
#### CRT_ABHA_106, resend OTP. `screen`
The resend control is disabled for 60 seconds after each send and works at most twice. Evidence: a recording or three timestamped screenshots: disabled at send, enabled after 60 seconds, disabled after the second resend. A resend that works reuses the `txnId`; a fresh `txnId` is a new request, not a resend.
#### CRT_ABHA_107, the OTP is verified, and a wrong OTP is refused. `sandbox`, `human`
Two observations, both needed.
1. A wrong OTP: `m1_post_v3_enrollment_enrol_byaadhaar_otp` returns 422 with `error.code` `ABDM-1204` and a message naming an invalid Aadhaar OTP value, and your screen says the OTP was wrong and lets the person retry without re-entering the Aadhaar number.
2. The right OTP: the 200 from CRT_ABHA_101 step 1.
The person enters both OTPs. Stop for each.
#### CRT_ABHA_108, the communication mobile is the Aadhaar mobile. `sandbox`, optional
If your system checks whether the mobile the person gave is the Aadhaar linked one and skips the second OTP when it is, drive that path. Exit condition: the journey reaches the ABHA address step with no `mobile-verify` call in the log. Record `not-run` if you always verify, which CRT_ABHA_109 then covers.
#### CRT_ABHA_109, a different communication mobile is verified. `sandbox`, `human`
Give a mobile that is not the Aadhaar linked one. Exit condition: `m1_post_v3_enrollment_request_otp_mobile_verify_create_ab_1b66bb` returns 200 with a `txnId`, the person enters the OTP, and `m1_post_v3_enrollment_auth_byabdm_mobile_verify_create_ab_091285` returns 200. A wrong OTP here is refused on screen and the person can retry.
#### CRT_ABHA_112, the ABHA address is suggested and validated. `sandbox` and `screen`
Mandatory for private and government integrators. Optional for a programme that creates a default address by demographic authentication.
Exit condition: `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp` returns 200 with `abhaAddressList` holding at least three entries, and `m1_post_v3_enrollment_enrol_abha_address_create_abha_aadhaar_otp` returns 200 with `preferredAbhaAddress` equal to the one chosen.
Screen check: at least three suggestions are shown, or a free field with these rules printed beside it, and an address already taken is refused with a message that says so and offers the suggestions.
```text
length 8 to 18
letters, digits, or both
at most one dot and at most one underscore, neither first nor last
```
#### CRT_ABHA_113, the ABHA number is displayed. `screen`
The 14 digit ABHA number and the ABHA address from CRT_ABHA_112 are shown together after creation. Evidence: the screen, with the number matching `healthIdNumber` from the pasted response.
#### CRT_ABHA_114, the ABHA card can be viewed and downloaded. `sandbox` and `screen`
Mandatory for private integrators that generate the card. Exit condition: `m1_get_v3_profile_account_abha_card` returns 200 with content type `image/png`, sent with the `X-token` from creation. Screen check: the card shown carries the ABHA number, the QR code, date of birth, gender and the ABHA address. The photo is optional.
#### CRT_ABHA_115, your own card carries the ABHA details. `screen`
For an integrator that prints its own programme card instead. Mandatory for government where CRT_ABHA_114 is not done. The card shows the ABHA number and address, and your system stores both against your own patient id. Evidence: the card and the stored record.
### CRT_ABHA_201 to 210: create an ABHA by Aadhaar biometric
Applies to every integrator. Optional, except that CRT_ABHA_209 is mandatory for a private integrator that generates the card and one of 209 or 210 is mandatory for government.
Loop limit: 3 passes per test case.
Same cases as 101 to 115 with these differences. Run each as its counterpart above and record the counterpart's evidence.
| Case | Counterpart | What differs |
|---|---|---|
| CRT_ABHA_201 | 101 | The creation call is `m1_post_v3_enrollment_enrol_byaadhaar_fingerprint` or `m1_post_v3_enrollment_enrol_byaadhaar_face`. Exit condition is the same 200 with `tokens.token` and `ABHAProfile.ABHANumber` |
| CRT_ABHA_205 | 105 and 107 | `human`: the person scans. A failed scan is refused on screen and can be retried. Face runs `m1_post_v3_enrollment_enrol_auth_init_create_abha_face_au_4dea33` then the capture call first |
| CRT_ABHA_206, 207 | 108, 109 | Unchanged |
| CRT_ABHA_208 | 113 | Unchanged |
| CRT_ABHA_209, 210 | 114, 115 | Unchanged |
| CRT_ABHA_202, 203, 204 | 102, 103, 104 | Unchanged |
Without a registered device the whole group is `not-run`, with that reason.
### CRT_ABHA_301 to 309: create an ABHA by demographic authentication
Government entities only. Mandatory for them. A private integrator records the group `not-run`.
Loop limit: 3 passes per test case.
#### CRT_ABHA_301 and 305, the demographic call creates the account. `sandbox`
One call, `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d`, with the name, date of birth, gender, Aadhaar number, mobile, address, state and district as they read on the Aadhaar card. Exit condition: 200 with `healthIdNumber`, `healthId` and `kycVerified` `true`.
Then the mismatch: send the same call with the date of birth changed. Exit condition: a 422 whose message says the information does not match the details on record with Aadhaar, and your screen says so without creating anything. Both observations, both pasted.
Nudge: the same mobile on a seventh account is refused with ABDM-1124. That is a mobile limit, not a demographic mismatch. Read `error.message`.
#### CRT_ABHA_305 address, the three cases. `sandbox` and `screen`
The address on the card and the profile is the same one in all three cases: the existing address when the person had one, the address they chose when they chose one, and the default `<ABHA number>@sbx` when neither. Evidence: `healthId` in the creation response and the address on `m1_get_v3_profile_account`, equal, for whichever case the run hit. Record which.
#### CRT_ABHA_306, the profile is filled from the response, not typed. `screen`
Name, date of birth and gender are shown filled and not editable. Address, mobile and email are editable. Evidence: the form after creation. `hiecm.concept.m1-confirm-the-filled-form`
#### CRT_ABHA_302, 303, 304, 307, 308, 309
As 102, 103, 104, 113, 114 and 115.
### CRT_ABHA_401 to 411: create an ABHA by driving licence or PAN
ABDM's M1 specification publishes no enrolment by document operation. Record every case in this group `not-run` with that reason, whatever your entity type, until an operation is published. Do not improvise a call.
### VRFY_ABHA_101 and 102: verify an ABHA number or address by Aadhaar OTP
Applies to every integrator. Mandatory.
Loop limit: 3 passes per test case.
**Preconditions.** A person present with an ABHA and the Aadhaar linked mobile.
#### VRFY_ABHA_101, by ABHA number. `sandbox`, `human`
Calls: `m1_post_v3_profile_login_request_otp_abha_number_aadhaar_otp`, the OTP, `m1_post_v3_profile_login_verify_abha_number_aadhaar_otp`, then `m1_get_v3_profile_account` with the returned token as `X-token`.
Exit condition: the verify returns 200 with `authResult` `success` and `accounts[0].ABHANumber`, and the profile call returns 200 with the same `ABHANumber`. A wrong OTP returns 400 with `otpValue` naming an invalid OTP value, and your screen says the verification failed.
Screen checks alongside: resend as in CRT_ABHA_106; name, date of birth and gender shown not editable; the profile stored against your own patient id after verification.
Nudge: the accounts array already carries the whole registration form. Fill the form from it before the profile call, so the person reads back rather than types.
#### VRFY_ABHA_102, by ABHA address. `sandbox`, `human`
Calls: `m1_post_v3_phr_web_login_abha_search_abha_address_login_a_de8184`, `m1_post_v3_phr_web_login_abha_request_otp_aadhaar_otp`, the OTP, `m1_post_v3_phr_web_login_abha_verify_aadhaar_otp`, then `m1_get_v3_phr_web_login_profile_abha_profile_abha_address_c04e83`.
Exit condition: the verify returns 200 with a token, and the profile call returns 200 carrying the ABHA number and the address searched for. Paste both.
### VRFY_ABHA_201 and 202: verify by the ABHA linked mobile OTP
Mandatory. As VRFY_ABHA_101 and 102 with the mobile OTP operations: `m1_post_v3_profile_login_request_otp_abha_number_abha_otp` and `m1_post_v3_profile_login_verify_abha_number_abha_otp` for the number, `m1_post_v3_phr_web_login_abha_request_otp_mobile_otp` and `m1_post_v3_phr_web_login_abha_verify_mobile_otp` for the address. Same exit conditions, same screen checks.
Loop limit: 3 passes per test case.
### VRFY_ABHA_301 to 305: fetch ABHA details by the communication mobile
Mandatory. This is the lookup that costs no Aadhaar OTP, so it is the one your desk should run first.
Loop limit: 3 passes per test case.
#### VRFY_ABHA_301, an invalid mobile is refused. `screen`
A nine digit and an eleven digit value are refused on screen with a message asking for a valid mobile, and nothing is sent.
#### VRFY_ABHA_302, no ABHA on this mobile. `sandbox`, `human`
Calls: `m1_post_v3_profile_login_request_otp_mobile`, the OTP, `m1_post_v3_profile_login_verify_mobile_otp`.
Exit condition: the verify returns 404 with `error.code` `ABDM-1114`. Your screen says no ABHA number is linked to this mobile, and offers to create one. The create offer is the point of the case; a dead end fails it. `hiecm.concept.m1-journey-completes-without-abha`
#### VRFY_ABHA_303, one or more ABHAs on this mobile. `sandbox`, `human`
Same two calls with a mobile that holds accounts, then `m1_post_v3_profile_login_verify_user` with the chosen `ABHANumber` and the first verify's token as `T-token`, then `m1_get_v3_profile_account`.
Exit condition: the first verify returns 200 with `accounts` holding at least one entry with `ABHANumber`, `preferredAbhaAddress`, `name` and `profilePhoto`; the user verify returns 200 with `token`; the profile call returns 200 with the chosen `ABHANumber`.
Screen checks: every account in the array is listed with name, number, address and photo; choosing one already linked to a patient in your system says so and names your patient id; choosing one not yet linked registers and links it. `hiecm.concept.m1-two-routes-to-the-profile`
Nudge: read the array as plural. One mobile can hold up to six accounts, and a screen that shows the first one silently links the wrong person.
#### VRFY_ABHA_304, a wrong OTP. `sandbox`, `human`
Exit condition: `m1_post_v3_profile_login_verify_mobile_otp` returns 400 with `otpValue` naming an invalid OTP value, and your screen says so and allows a retry without re-entering the mobile.
#### VRFY_ABHA_305, resend. `screen`
As CRT_ABHA_106.
### VRFY_ABHA_401 to 405: fetch ABHA details by Aadhaar number
Mandatory.
Loop limit: 3 passes per test case.
#### VRFY_ABHA_401, an invalid Aadhaar is refused. `screen`
As CRT_ABHA_104, on the verification screen.
#### VRFY_ABHA_403, no ABHA on this Aadhaar. `sandbox`, `human`
Calls: `m1_post_v3_profile_login_request_otp_aadhaar`, the OTP, `m1_post_v3_profile_login_verify_aadhaar_otp`.
Exit condition: the request returns 200 with a `message` naming the masked Aadhaar mobile, the person enters the OTP, and the verify returns 404 with `error.code` `ABDM-1114` and a message that no ABHA user is registered with this Aadhaar number. Your screen says so and offers creation, and the creation path does not ask for the Aadhaar number again. `hiecm.concept.m1-never-ask-twice`
#### VRFY_ABHA_404, an ABHA exists on this Aadhaar. `sandbox`, `human`
Same calls with an Aadhaar that holds an account, then `m1_get_v3_profile_account`.
Exit condition: the verify returns 200 with `authResult` `success` and `accounts[0].ABHANumber`; the profile call returns 200 with the same number. Screen checks: the profile is shown, the card can be downloaded as in CRT_ABHA_114, and the account is registered and linked to your patient id if it was not already.
#### VRFY_ABHA_402 and 405
A wrong OTP and resend, as VRFY_ABHA_304 and 305.
### VRFY_ABHA_501 and 502: verify by fingerprint, government entities
Optional. `human`. Calls: `m1_post_v3_1_profile_login_verify_fingerprint` for a number. The set names no operation for an address by fingerprint; record VRFY_ABHA_502 `not-run` with that reason. Exit condition for 501: 200 with `accounts[0].ABHANumber`, then the profile call as VRFY_ABHA_101. Without a device, `not-run`.
Loop limit: 3 passes per test case.
### Reading the ABHA QR code, optional
The set numbers this row VRFY_ABHA_501 as well. Record it as `QR-read` so the two are told apart in the manifest.
`screen`. No call. Your system scans a person's ABHA QR code and fills name, date of birth, gender and mobile into registration from it. Evidence: the filled form after a scan, with nothing typed. `hiecm.concept.m1-two-routes-to-the-profile`
Loop limit: 3 passes per test case.
### PROF_ABHA_601 to 605: profile update
Optional. Each is `sandbox` and `human`, since every change is confirmed by an OTP.
Loop limit: 3 passes per test case.
| Case | Calls | Exit condition |
|---|---|---|
| PROF_ABHA_601, mobile | `m1_post_v3_profile_account_request_otp_update_mobile`, the OTP, `m1_post_v3_profile_account_verify_update_mobile` | The verify returns 200 with `authResult` `success` and a message that the mobile number was linked; `m1_get_v3_profile_account` then shows the new masked `mobile` |
| PROF_ABHA_602, photo | `m1_patch_v3_profile_account_profile_photo` | 200, and `m1_get_v3_profile_account` returns the new `profilePhoto` |
| PROF_ABHA_603, email | The request and verify calls above with the email scope | 200 on the verify, and the profile shows the new email |
| PROF_ABHA_604, re-KYC | `m1_post_v3_profile_account_request_otp_re_kyc`, the OTP, `m1_post_v3_profile_account_verify_re_kyc` | 200 on the verify, and the profile shows `kycVerified` `true` |
| PROF_ABHA_605, delete or deactivate | ABDM's M1 specification publishes no delete or deactivate operation | `not-run`, with that reason |
### TAGGING_UNIQUEPATIENTID_UNIQUEABHANUMBER: one ABHA per patient id
Mandatory. `screen`. Your system checks whether an ABHA number already exists in its database before it creates a patient id, on both the creation and the verification paths, so one ABHA never maps to two of your patients and one of your patients never holds two ABHAs.
Evidence: verify the same ABHA twice in a run. The second time, the screen says the record is already linked and names your patient id, and the database holds one row. `hiecm.concept.m1-avoiding-duplicate-abha`
Loop limit: 3 passes per test case.
### SHARE_PATIENT_PROFILE_701: share a profile by scanning the facility QR
This case is run from the person's ABHA app, and the call that proves it lands on your side as the `on-share` webhook. It belongs to the scan and register skill, `abdm-scan-and-register`, which carries the webhook and the token reply. Record it there. In this manifest, write `see abdm-scan-and-register`.
## When the last case is recorded
Write the manifest. It is the whole report. Case ids to request ids, nothing else, so a reviewer checks it against the gateway's own record rather than reading screenshots. Secrets never appear in it: no tokens, no OTPs, no Aadhaar numbers, no client secret.
```json
{
"skill": "abdm-m1",
"set": "M1 ABHA creation and verification, NHA, version 1.1",
"entity": "private",
"run_started": "2026-09-29T10:12:40Z",
"run_finished": "2026-09-29T11:48:03Z",
"client_id": "<your sandbox client id>",
"cases": [
{"id": "CRT_ABHA_101", "outcome": "passed", "evidence": "sandbox",
"request_ids": ["6f1c...", "a02d...", "c9e4..."],
"observed": ["byAadhaar 200 message=Account created successfully", "abha-address 200 healthIdNumber", "profile/account 200 ABHANumber"]},
{"id": "CRT_ABHA_102", "outcome": "passed", "evidence": "screen",
"attachment": "consent-2026-09-29.png", "rule": "hiecm.concept.m1-screen-contract"},
{"id": "CRT_ABHA_105", "outcome": "needs-human", "evidence": "human",
"request_ids": ["b7a1..."], "reason": "OTP arrives on the person's phone; nobody present"},
{"id": "CRT_ABHA_401", "outcome": "not-run", "reason": "no enrolment by document operation in the M1 specification"}
]
}
```
Then say, in three lines: how many cases passed, failed, need a human and were not run; which mandatory cases for your entity are not `passed`; and the one case to fix first.

View File

@ -0,0 +1,92 @@
---
name: abdm-m2
description: Use when building, debugging or testing ABDM Milestone 2: care contexts, HIP initiated linking, discovery, and pushing encrypted health records to a requester. Carries the endpoints and the encryption parameters. Also carries the scaffolding loop that builds it flow by flow and the loop from a failed call to a named fix, in references/.
type: skill
domain: m2
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- hip-registration
- callback-url
- nrces-document-bundle
produces:
- care-context
- link-token
- health-information-push
can_execute: true
can_orchestrate: false
---
# ABDM M2, create and link records
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m2/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M2
- Tell ABDM a patient had a visit with you, so their records can be found later.
- Answer a discovery request when somebody looks for that patient.
- Send records out encrypted when a consent says you must.
- Get the link token the linking calls need.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Link a care context for this patient"
- "What has to be in place before my first M2 call?"
- "My M2 call returned ABDM-1000. What is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 24 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 120 error codes from the specification's examples. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- You act as the HIP.
- Before M2, three things must hold: the facility is registered in the HFR with a valid HFR ID, that HIP ID is linked to your client ID (through the NHPR portal or the M4 software linkage APIs), and a callback URL is set for the client ID with the Update Bridge API.
- You are the side that encrypts, and the parameters arrive from the requester rather than from you. The health information request carries `keyMaterial` with `cryptoAlg`, `curve: Curve25519`, the requester's `dhPublicKey` and a `nonce`. Generate your own Curve25519 pair and your own nonce, and send your public key and nonce back with the data so the requester can derive the same secret.
- The key derivation and the symmetric cipher applied over that shared secret are not in the specification. The data flow page (/docs/hiecm/v3/concepts/data-flow) and the Fidelius reference (shared.concept.fidelius-ecdh-interop) give the scheme: HKDF over the shared secret, and AES-GCM for the payload. Build from those, not from a sample.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m2
- The flows as diagrams: /docs/hiecm/v3/milestones/m2
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,860 @@
# HIE-CM m2 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### ABDM-1000
**NHA's list for the module:** `Unable to connect the database`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1001
**NHA's list for the module:** `No data found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1004
**NHA's list for the module:** `SMS Gateway is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Bad Request, invalid request Body`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1007
**NHA's list for the module:** `Connection failed due to timeout`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1008
**NHA's list for the module:** `SMS service currently disabled`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1010
**NHA's list for the module:** `Validation failed`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1011
**NHA's list for the module:** `Gateway database unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1012
**NHA's list for the module:** `No records found against the ABHA Address`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1013
**NHA's list for the module:** `Invalid ABHA Number`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1015
**NHA's list for the module:** `Invalid Response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1016
**NHA's list for the module:** `Invalid TimeStamp`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1017
**NHA's list for the module:** `Invalid TransactionId`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1018
**NHA's list for the module:** `Share Profile database unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1019
**NHA's list for the module:** `Dependent Service Unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1020
**NHA's list for the module:** `Unknown database`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1022
**NHA's list for the module:** `Too many requests`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1023
**NHA's list for the module:** `Invalid User`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1024
**NHA's list for the module:** `Dependent service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1025
**NHA's list for the module:** `Invalid ServiceId`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1026
**NHA's list for the module:** `Invalid Link Token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1027
**NHA's list for the module:** `You are blocked. Please try again after 24 hours.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1028
**NHA's list for the module:** `HIP is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1029
**NHA's list for the module:** `Redis server is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1030
**NHA's list for the module:** `Invalid request ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1031
**NHA's list for the module:** `Invalid request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1032
**NHA's list for the module:** `Invalid header`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1033
**NHA's list for the module:** `HIU is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1034
**NHA's list for the module:** `Notification service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1035
**NHA's list for the module:** `Invalid HIP ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1036
**NHA's list for the module:** `Data does not matched`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1037
**NHA's list for the module:** `Counter and Care context count mismatch`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1038
**NHA's list for the module:** `ABHA address and Link token mismatch`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1040
**NHA's list for the module:** `Invalid HIU ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1041
**NHA's list for the module:** `Invalid Acknowledgement`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1042
**NHA's list for the module:** `Provider Mandatory`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1043
**NHA's list for the module:** `ABHA Address does not match with KYC details.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1044
**NHA's list for the module:** `Broadcast Failed`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1045
**NHA's list for the module:** `Database Access is restricted`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1046
**NHA's list for the module:** `Invalid Purpose`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1047
**NHA's list for the module:** `Purpose does not exist`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1048
**NHA's list for the module:** `Timeout`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1049
**NHA's list for the module:** `Invalid Profile Share Intent Keys`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1050
**NHA's list for the module:** `Invalid Profile Share Metadata Keys`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1051
**NHA's list for the module:** `Invalid ABHA Number or ABHA Address`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1052
**NHA's list for the module:** `Invalid TransactionId or response's requestId`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1401
**NHA's list for the module:** `HIP is not available`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1061
**NHA's list for the module:** `Consent artefact expired`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1062
**NHA's list for the module:** `Consent Not granted`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1063
**NHA's list for the module:** `Date Range given is invalid`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1064
**NHA's list for the module:** `request with this request id already exists`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1109
**NHA's list for the module:** `ABHA DB service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1108
**NHA's list for the module:** `Notification DB service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1201
**NHA's list for the module:** `IDP Gateway is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `Unknown exception`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Invalid combinations of scopes`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1056
**NHA's list for the module:** `This care contexts has been already linked`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1055
**NHA's list for the module:** `Invalid HIP Id or PHR Id`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1056
**NHA's list for the module:** `Invalid Link Reference Number`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1057
**NHA's list for the module:** `Invalid Care Contexts`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1059
**NHA's list for the module:** `Invalid Care Contexts count`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1060
**NHA's list for the module:** `Invalid Patient Reference Number`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1061
**NHA's list for the module:** `Invalid Patient Display`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1062
**NHA's list for the module:** `ABHA number mismatch with Link token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1063
**NHA's list for the module:** `HIP Id mismatch with Link token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1064
**NHA's list for the module:** `Request body was missing`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1065
**NHA's list for the module:** `Invalid X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1066
**NHA's list for the module:** `Invalid JWT token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1067
**NHA's list for the module:** `Request body not required`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1170
**NHA's list for the module:** `Invalid ABHA address`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1084
**NHA's list for the module:** `ABHA address mismatch with X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1085
**NHA's list for the module:** `ABHA number mismatch with X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1086
**NHA's list for the module:** `Patient profile mismatch with X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1087
**NHA's list for the module:** `Duplicate patient share request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1090
**NHA's list for the module:** `Duplicate HIP link request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1091
**NHA's list for the module:** `Duplicate Get links request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1092
**NHA's list for the module:** `Duplicate Link token request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1093
**NHA's list for the module:** `Duplicate Bridge request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1094
**NHA's list for the module:** `Duplicate bridge patch request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1095
**NHA's list for the module:** `Duplicate Bridge service request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1102
**NHA's list for the module:** `Profile information cannot be null`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1103
**NHA's list for the module:** `Duplicate Discovery request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1104
**NHA's list for the module:** `Duplicate Init request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1105
**NHA's list for the module:** `Duplicate Confirm request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1106
**NHA's list for the module:** `Duplicate On discovery request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1107
**NHA's list for the module:** `Duplicate On init request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1108
**NHA's list for the module:** `Duplicate On confirm request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1109
**NHA's list for the module:** `Invalid On discovery response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1110
**NHA's list for the module:** `Invalid On init response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1111
**NHA's list for the module:** `Invalid On confirm response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1112
**NHA's list for the module:** `Invalid or already expired consent artefact id`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1113
**NHA's list for the module:** `Duplicate health information provider data flow response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1149
**NHA's list for the module:** `Intent type is not supported at HIP end`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1150
**NHA's list for the module:** `Bridge API version cannot be null`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1402
**NHA's list for the module:** `Acknowledgement is not received from HIP`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `HIP is unable to generate a token at this time. Please try again later.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `HIP is unable to process at this time. Please try again later.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1030
**NHA's list for the module:** `Request id not found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2500
**NHA's list for the module:** `No mapping found for`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2401
**NHA's list for the module:** `The X Auth token is invalid.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2402
**NHA's list for the module:** `Invalid Timestamp`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2404
**NHA's list for the module:** `Invalid Request Id`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2429
**NHA's list for the module:** `Too many requests found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2501
**NHA's list for the module:** `Payment status should be : SUCCESS,CANCELED,PENDING,FAIL,REFUND_INITIATED,REFUND_SUCCESS`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2500
**NHA's list for the module:** `Authorization header is missing`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2403
**NHA's list for the module:** `Invalid X-CM-ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2406
**NHA's list for the module:** `Invalid API sequence flow, please follow logical flow`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2406
**NHA's list for the module:** `The status is invalid. Please follow the logical status flow or transition.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `User not found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `Cannot process the request at the moment, please try later.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9001
**NHA's list for the module:** `No open order against ABHA. Please ensure a minimum of one open order`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9002
**NHA's list for the module:** `No registration found at <<hospital name>>. Contact counter support`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9003
**NHA's list for the module:** `Hospital services temporarily unavailable. Please try again after some time.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9004
**NHA's list for the module:** `Services disrupted, please try again.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9005
**NHA's list for the module:** `Bank server not responding. Please try again later`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9006
**NHA's list for the module:** `Service details mismatch. Please ensure original service ID from HMIS`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9007
**NHA's list for the module:** `The Scan and Pay functionality is not enabled at this facility. Kindly contact the hospital administration.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Invalid count, must be 2 digit and ranges between 1 to 20`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1407
**NHA's list for the module:** `The ABHA Number associated with this ABHA Address is currently deactivated. Please reactivate it.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Invalid HIType, it must be in Prescription,DiagnosticReport,OPConsultation,DischargeSummary,ImmunizationRecord,HealthDocumentRecord,WellnessRecord,Invoice`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/m2
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `ABDM-1000` | | Unable to connect the database | NHA's list for the module |
| `ABDM-1001` | | No data found | NHA's list for the module |
| `ABDM-1004` | | SMS Gateway is unavailable | NHA's list for the module |
| `ABDM-1006` | | Bad Request, invalid request Body | NHA's list for the module |
| `ABDM-1007` | | Connection failed due to timeout | NHA's list for the module |
| `ABDM-1008` | | SMS service currently disabled | NHA's list for the module |
| `ABDM-1010` | | Validation failed | NHA's list for the module |
| `ABDM-1011` | | Gateway database unavailable | NHA's list for the module |
| `ABDM-1012` | | No records found against the ABHA Address | NHA's list for the module |
| `ABDM-1013` | | Invalid ABHA Number | NHA's list for the module |
| `ABDM-1015` | | Invalid Response | NHA's list for the module |
| `ABDM-1016` | | Invalid TimeStamp | NHA's list for the module |
| `ABDM-1017` | | Invalid TransactionId | NHA's list for the module |
| `ABDM-1018` | | Share Profile database unavailable | NHA's list for the module |
| `ABDM-1019` | | Dependent Service Unavailable | NHA's list for the module |
| `ABDM-1020` | | Unknown database | NHA's list for the module |
| `ABDM-1022` | | Too many requests | NHA's list for the module |
| `ABDM-1023` | | Invalid User | NHA's list for the module |
| `ABDM-1024` | | Dependent service unavailable | NHA's list for the module |
| `ABDM-1025` | | Invalid ServiceId | NHA's list for the module |
| `ABDM-1026` | | Invalid Link Token | NHA's list for the module |
| `ABDM-1027` | | You are blocked. Please try again after 24 hours. | NHA's list for the module |
| `ABDM-1028` | | HIP is unavailable | NHA's list for the module |
| `ABDM-1029` | | Redis server is unavailable | NHA's list for the module |
| `ABDM-1030` | | Invalid request ID | NHA's list for the module |
| `ABDM-1031` | | Invalid request | NHA's list for the module |
| `ABDM-1032` | | Invalid header | NHA's list for the module |
| `ABDM-1033` | | HIU is unavailable | NHA's list for the module |
| `ABDM-1034` | | Notification service unavailable | NHA's list for the module |
| `ABDM-1035` | | Invalid HIP ID | NHA's list for the module |
| `ABDM-1036` | | Data does not matched | NHA's list for the module |
| `ABDM-1037` | | Counter and Care context count mismatch | NHA's list for the module |
| `ABDM-1038` | | ABHA address and Link token mismatch | NHA's list for the module |
| `ABDM-1040` | | Invalid HIU ID | NHA's list for the module |
| `ABDM-1041` | | Invalid Acknowledgement | NHA's list for the module |
| `ABDM-1042` | | Provider Mandatory | NHA's list for the module |
| `ABDM-1043` | | ABHA Address does not match with KYC details. | NHA's list for the module |
| `ABDM-1044` | | Broadcast Failed | NHA's list for the module |
| `ABDM-1045` | | Database Access is restricted | NHA's list for the module |
| `ABDM-1046` | | Invalid Purpose | NHA's list for the module |
| `ABDM-1047` | | Purpose does not exist | NHA's list for the module |
| `ABDM-1048` | | Timeout | NHA's list for the module |
| `ABDM-1049` | | Invalid Profile Share Intent Keys | NHA's list for the module |
| `ABDM-1050` | | Invalid Profile Share Metadata Keys | NHA's list for the module |
| `ABDM-1051` | | Invalid ABHA Number or ABHA Address | NHA's list for the module |
| `ABDM-1052` | | Invalid TransactionId or response's requestId | NHA's list for the module |
| `ABDM-1401` | | HIP is not available | NHA's list for the module |
| `ABDM-1061` | | Consent artefact expired | NHA's list for the module |
| `ABDM-1062` | | Consent Not granted | NHA's list for the module |
| `ABDM-1063` | | Date Range given is invalid | NHA's list for the module |
| `ABDM-1064` | | request with this request id already exists | NHA's list for the module |
| `ABDM-1109` | | ABHA DB service unavailable | NHA's list for the module |
| `ABDM-1108` | | Notification DB service unavailable | NHA's list for the module |
| `ABDM-1201` | | IDP Gateway is unavailable | NHA's list for the module |
| `ABDM-9999` | | Unknown exception | NHA's list for the module |
| `ABDM-1006` | | Invalid combinations of scopes | NHA's list for the module |
| `ABDM-1056` | | This care contexts has been already linked | NHA's list for the module |
| `ABDM-1055` | | Invalid HIP Id or PHR Id | NHA's list for the module |
| `ABDM-1056` | | Invalid Link Reference Number | NHA's list for the module |
| `ABDM-1057` | | Invalid Care Contexts | NHA's list for the module |
| `ABDM-1059` | | Invalid Care Contexts count | NHA's list for the module |
| `ABDM-1060` | | Invalid Patient Reference Number | NHA's list for the module |
| `ABDM-1061` | | Invalid Patient Display | NHA's list for the module |
| `ABDM-1062` | | ABHA number mismatch with Link token | NHA's list for the module |
| `ABDM-1063` | | HIP Id mismatch with Link token | NHA's list for the module |
| `ABDM-1064` | | Request body was missing | NHA's list for the module |
| `ABDM-1065` | | Invalid X Auth token | NHA's list for the module |
| `ABDM-1066` | | Invalid JWT token | NHA's list for the module |
| `ABDM-1067` | | Request body not required | NHA's list for the module |
| `ABDM-1170` | | Invalid ABHA address | NHA's list for the module |
| `ABDM-1084` | | ABHA address mismatch with X Auth token | NHA's list for the module |
| `ABDM-1085` | | ABHA number mismatch with X Auth token | NHA's list for the module |
| `ABDM-1086` | | Patient profile mismatch with X Auth token | NHA's list for the module |
| `ABDM-1087` | | Duplicate patient share request | NHA's list for the module |
| `ABDM-1090` | | Duplicate HIP link request | NHA's list for the module |
| `ABDM-1091` | | Duplicate Get links request | NHA's list for the module |
| `ABDM-1092` | | Duplicate Link token request | NHA's list for the module |
| `ABDM-1093` | | Duplicate Bridge request | NHA's list for the module |
| `ABDM-1094` | | Duplicate bridge patch request | NHA's list for the module |
| `ABDM-1095` | | Duplicate Bridge service request | NHA's list for the module |
| `ABDM-1102` | | Profile information cannot be null | NHA's list for the module |
| `ABDM-1103` | | Duplicate Discovery request | NHA's list for the module |
| `ABDM-1104` | | Duplicate Init request | NHA's list for the module |
| `ABDM-1105` | | Duplicate Confirm request | NHA's list for the module |
| `ABDM-1106` | | Duplicate On discovery request | NHA's list for the module |
| `ABDM-1107` | | Duplicate On init request | NHA's list for the module |
| `ABDM-1108` | | Duplicate On confirm request | NHA's list for the module |
| `ABDM-1109` | | Invalid On discovery response | NHA's list for the module |
| `ABDM-1110` | | Invalid On init response | NHA's list for the module |
| `ABDM-1111` | | Invalid On confirm response | NHA's list for the module |
| `ABDM-1112` | | Invalid or already expired consent artefact id | NHA's list for the module |
| `ABDM-1113` | | Duplicate health information provider data flow response | NHA's list for the module |
| `ABDM-1149` | | Intent type is not supported at HIP end | NHA's list for the module |
| `ABDM-1150` | | Bridge API version cannot be null | NHA's list for the module |
| `ABDM-1402` | | Acknowledgement is not received from HIP | NHA's list for the module |
| `ABDM-9999` | | HIP is unable to generate a token at this time. Please try again later. | NHA's list for the module |
| `ABDM-9999` | | HIP is unable to process at this time. Please try again later. | NHA's list for the module |
| `ABDM-1030` | | Request id not found | NHA's list for the module |
| `ABDM-2500` | | No mapping found for | NHA's list for the module |
| `ABDM-2401` | | The X Auth token is invalid. | NHA's list for the module |
| `ABDM-2402` | | Invalid Timestamp | NHA's list for the module |
| `ABDM-2404` | | Invalid Request Id | NHA's list for the module |
| `ABDM-2429` | | Too many requests found | NHA's list for the module |
| `ABDM-2501` | | Payment status should be : SUCCESS,CANCELED,PENDING,FAIL,REFUND_INITIATED,REFUND_SUCCESS | NHA's list for the module |
| `ABDM-2500` | | Authorization header is missing | NHA's list for the module |
| `ABDM-2403` | | Invalid X-CM-ID | NHA's list for the module |
| `ABDM-2406` | | Invalid API sequence flow, please follow logical flow | NHA's list for the module |
| `ABDM-2406` | | The status is invalid. Please follow the logical status flow or transition. | NHA's list for the module |
| `ABDM-9999` | | User not found | NHA's list for the module |
| `ABDM-9999` | | Cannot process the request at the moment, please try later. | NHA's list for the module |
| `ABDM-9001` | | No open order against ABHA. Please ensure a minimum of one open order | NHA's list for the module |
| `ABDM-9002` | | No registration found at <<hospital name>>. Contact counter support | NHA's list for the module |
| `ABDM-9003` | | Hospital services temporarily unavailable. Please try again after some time. | NHA's list for the module |
| `ABDM-9004` | | Services disrupted, please try again. | NHA's list for the module |
| `ABDM-9005` | | Bank server not responding. Please try again later | NHA's list for the module |
| `ABDM-9006` | | Service details mismatch. Please ensure original service ID from HMIS | NHA's list for the module |
| `ABDM-9007` | | The Scan and Pay functionality is not enabled at this facility. Kindly contact the hospital administration. | NHA's list for the module |
| `ABDM-1006` | | Invalid count, must be 2 digit and ranges between 1 to 20 | NHA's list for the module |
| `ABDM-1407` | | The ABHA Number associated with this ABHA Address is currently deactivated. Please reactivate it. | NHA's list for the module |
| `ABDM-1006` | | Invalid HIType, it must be in Prescription,DiagnosticReport,OPConsultation,DischargeSummary,ImmunizationRecor… | NHA's list for the module |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,639 @@
# Design M2, create and link records
What the integration has to do to the journey around the calls: how many questions a patient is asked, where a failure is shown, and what a screen is forbidden to claim. Every rule below comes from a Catalogue atom, cited at the end.
## The unit of work is an exchange, not a call
### In plain words
Every M2 and M3 call answers twice. Once straight away, saying the request was
accepted, and later on a callback to your own bridge, saying what actually
happened. Do not treat the first as success. It says the request was accepted,
not that anything was linked.
So the request id stops being a log handle and becomes the join key. An
outbound call, the wait, and the callback that answers it are one exchange, and
they should read as one row with a timeline. Shown as three separate lines they
read as noise, and the difference between accepted and done stays invisible,
which is the one thing the interface exists to show.
### What happens
Register the waiter before sending. A callback that arrives before the HTTP
response returns is a race the sandbox eventually wins.
Then hold five outcomes apart. These must not look alike:
| State | Means | Observed |
|---|---|---|
| sent | in the air | |
| accepted, waiting | `202`, nothing back yet | real patient, 340 ms |
| answered | the callback arrived | |
| refused before waiting | a synchronous `400`, nothing was ever pending | fictional patient, 91 ms |
| no answer in the window | accepted, then silence | real patient, 60,000 ms |
The fourth and fifth are the ones implementations collapse into each other. A
refused call closing its waiter through the same function as an answered one
renders a synchronous `400` as a green callback arrived. Fix that in the shared
function rather than at the call sites that reach it.
The fourth outcome is also useful. Generate link token validates the patient
before accepting the request, so an identity nobody holds is refused at once
with `400` and `ABDM-9999`, while a recognised one is accepted with `202` and
answers later. The two are distinguishable at the first response, so only a
recognised patient is worth waiting for. Note the code: `ABDM-9999` is the catch
all, and the message is the only part naming the cause.
No timeout is published for any of these, so the honest words for the fifth
outcome are that nothing arrived in the window you chose.
### How you know it worked
Send one linking call and read one row. It carries the request id, the outbound
call, the elapsed wait, and the callback when it lands, in one timeline.
Send a call for an identity nobody holds. The row shows refused within a few
hundred milliseconds, never shows waiting, and no waiter is left open.
### When it goes wrong
- A synchronous refusal shows as a completed exchange. The refusal path and the
callback path share the function that closes the waiter.
- A callback cannot be matched to its call. The waiter was registered after
sending, and the callback won the race.
- Every call is accepted and nothing ever answers. No callback URL is registered
against the facility id, which is a configuration cause for a runtime symptom.
## Nobody is standing there, so never block and never lose the state
### In plain words
M1's rules do not carry over to M2. There is no patient to spare a question, no
one time password to save, and no screen anybody is looking at. The patient has
left. What there is instead is a call that was accepted and then went quiet, and
a callback that may arrive tomorrow or never.
Two consequences follow, and they are the opposite of how an M1 journey is
built.
### What happens
**Never block.** Linking is several calls, each answered on a callback that may
be a minute away. A button that waits for the outcome is a button that hangs for
sixty seconds and then says nothing useful. Start the work, return at once, and
point at where it can be watched. The desk gets on with the next patient.
**State outlives the session.** Where a link has got to belongs on the record,
not in a page's memory. Reopening the application must show where everything
stands without re-running anything. This is the reverse of M1, where the whole
journey lives and dies inside one person's visit.
**Name the step, not the spinner.** Linking tells a receptionist nothing. These
do:
- Asking ABDM for a link token.
- Asking ABDM for a link token: ABDM accepted it and no answer came back. This
client has no callback URL registered, so the answer has nowhere to go.
- Asking ABDM for a link token: refused. `ABDM-9999`, user not found.
The second is the interesting one. It names a configuration cause for a runtime
symptom, which is the difference between a receptionist raising a ticket and an
integrator fixing one line of configuration.
### How you know it worked
Start a link and close the page. Reopen the application and the record shows
which step the link reached, and when, without any call being made again.
Press the button that starts linking. It returns immediately and points at where
progress can be watched. It does not sit waiting for a callback.
### When it goes wrong
- The interface hangs for sixty seconds and then reports nothing. Something is
waiting on the callback inside the request that started the work.
- Reopening the application shows a link as not started when it was started
yesterday. The state was held in the page rather than on the record.
- A receptionist raises a ticket for what turns out to be a missing callback
URL. The message named the symptom without naming the configuration cause.
## A refused request is still remembered, so a retry is a duplicate
### In plain words
ABDM records a link token request even when it refuses the patient, and
deduplicates the next identical request against the one it refused. So a retry
inside that window cannot succeed, and its refusal no longer names the original
cause.
The second message says duplicate, which reads as though the first attempt had
worked. It had not.
### What happens
The same request for the same unknown address, sent twice:
| When | Result |
|---|---|
| 14:17:42 | `400`, `ABDM-9999`, user not found |
| 14:18:51, 69 seconds later | `400`, `ABDM-1092`, duplicate link token request |
The length of the deduplication window is not published. It is at least sixty
nine seconds.
So do not offer a button that cannot work. Where a duplicate is the answer,
explain it, and say that the earlier refusal is the one that names the cause.
Point the reader back at the first exchange rather than at the second.
This is the reason the first refusal has to be kept rather than replaced. An
interface that overwrites the last error with the newest one destroys the only
message that explained anything.
### How you know it worked
Send a request that is refused, then send it again inside the window. The
interface shows `ABDM-1092`, does not present it as a new failure, and links
back to the first exchange and its `ABDM-9999`.
The retry control is absent or disabled while the window is open, rather than
present and failing.
### When it goes wrong
- A retry loop runs and every attempt after the first says duplicate. Nothing is
checking whether a retry can succeed.
- The cause of a failure cannot be found because the newest message replaced it.
Keep every exchange, and treat the first refusal as the authoritative one.
- A duplicate refusal is read as evidence that the first request succeeded.
It is not. The first was refused, and ABDM remembered the refusal.
## What the integrator needs on screen, separately from the patient
### In plain words
A desk that talks to ABDM asynchronously cannot be debugged without a view built
for the integrator rather than the receptionist. It is cheap to build and it is
the difference between a fix that takes a minute and one that takes a day.
### What happens
**A live view of every call and callback, grouped by request id**, so an
outbound call, its wait, and the callback answering it read as one exchange with
a timeline.
**Redact by name and length, never by value.** Show that an `Authorization`
header was sent and how long it was. Never its contents. The panel is a browser.
**Show what would be sent, before sending it.** A request preview or a bundle
inspector turns it failed at the far end into something checkable at the desk.
**A readiness check that names what is missing**, and this is where two
configuration failures have to be told apart:
| Missing | Effect | Treat as |
|---|---|---|
| Facility id | the call cannot be sent | blocker, refuse locally |
| Callback URL | the call sends and is accepted, and the answer has nowhere to go | warning, send anyway |
Treating both as blockers is the tempting mistake. Refusing to send when no
callback URL is registered hides exactly the behaviour the panel exists to make
visible. Send, accept, wait, time out, and say why.
Refuse the first locally and name the value. An invented facility id comes back
from ABDM as an entitlement error that reads like a credentials problem, and
sends the integrator to the console instead of to one line of configuration.
Presence is not validity. `X-HIU-ID` is checked for presence rather than
against any registry: absent is refused `401`, and an invented id is accepted
`202`. Anyone testing with a made up id sees acknowledgements that look like
progress, which is why the local check has to be the strict one.
### How you know it worked
Clear the facility id and press the button. The call is refused before anything
is sent, and the message names the configuration value that is absent.
Clear the callback URL and press the same button. The call is sent, accepted,
waits the window, and reports that nothing arrived and why.
Open the panel during a linking run. Each exchange is one row, headers are shown
by name and length, and no header value appears anywhere.
### When it goes wrong
- A token appears in the panel. Redaction is by value rather than by name and
length. This is a credential leak, not a display bug.
- An integrator is sent to the developer console by an entitlement error. The
facility id was not checked locally before the call went out.
- Calls are accepted and nothing ever answers, and the panel says only waiting.
Name the missing callback URL as the cause, rather than leaving the reader
with a timeout.
## Inbound is a surface, and silence there is a failure state
### In plain words
M2 and M3 are not only things your desk asks ABDM. ABDM asks your desk things
and waits: a discovery request, a link initiation, a link confirmation, a
request for the records a consent covers, and a consent notification.
Four of those five carry no documented payload in the published sources. A
handler written against an assumed shape fails on the first real delivery,
asynchronously, where nobody is watching. And a handler that only logs leaves a
patient's records undiscoverable while appearing to work.
### What happens
Answer the transport quickly and reason afterwards. ABDM is waiting on a `2xx`,
and a handler that thinks before it replies is a handler that times out.
Then give the interface a state for ABDM asked and this desk has not replied.
Not an empty list, and not silence. A named, visible condition, saying what a
correct reply would have been.
Where the payload is undocumented, record the entire body and the entire header
set on the first delivery of each path, before anything tries to read it.
Record header names and lengths, never values. The name is the finding. The
value is a credential.
Every inbound callback carries a bearer token in the `Authorization` header, as
`Bearer <token>`. Verify it before your handler does any work. See
[proving a callback came from ABDM](/docs/hiecm/v3/concepts/callback-authenticity).
The security rule underneath: a URL reachable by ABDM is reachable by everyone.
A presented signature that fails verification is refused everywhere. An absent
one may be tolerated on a sandbox, and where it is, the record has to be marked
as unverified rather than passed off as genuine.
### How you know it worked
Trigger a discovery against your bridge. The transport is acknowledged inside
the window, and the interface shows the delivery, the reply, and the time
between them.
Take a path you have never received before. The whole body and the whole header
name set are recorded before any code reads a field, so the shape can be learned
from the record rather than guessed.
Leave an inbound request unanswered on purpose. The interface names it as
unanswered rather than showing nothing.
### When it goes wrong
- A patient's records cannot be found from another facility, and nothing looks
broken. A discovery handler is logging and not replying.
- Deliveries time out under load. Work is being done before the acknowledgement
rather than after it.
- A handler throws on the first real delivery. It was written against an assumed
payload, and the body was never recorded whole.
## A care context and its records are one thing
### In plain words
A care context is the visit. The records are what happened at it. Neither is
useful alone.
A bundle nobody can name a care context for cannot be sent. A care context with
nothing behind it is a promise the next discovery cannot keep: another facility
finds the visit, asks for the records, and there are none.
So store them together and show them together.
### What happens
Show the counts that matter to a patient rather than the ones that are easy to
compute: visits opened, documents attached, and visits not yet findable
elsewhere. The third is the one worth surfacing, because it means a record
exists that no other facility can reach.
Validate before storing, not before sending. Refusing to store a bundle the
receiver could not read is cheap. Discovering it after a consent has been served
is not.
Let the desk inspect a bundle before it goes: the profile it claims, the
resources inside it, the attachment type, and whether every reference resolves
within the bundle. The upload itself wants a drop target, a title prefilled from
the filename but editable, and named refusals for type and for size. Show a hash
and a size so a file is identifiable in a list without keeping a second copy of
it.
Write the title for the patient. They read it in their own application months
later, not the receptionist filing it today.
Never log the attachment. It is a patient's record, and it belongs in the bundle
and in the encrypted payload built from it, nowhere else.
### How you know it worked
Open a visit and attach nothing. The interface counts it as a visit not yet
findable elsewhere, rather than as a completed link.
Attach a document and the same count falls by one.
Try to store a bundle whose references do not resolve inside it. Storing is
refused, and the refusal names the reference that pointed outward.
### When it goes wrong
- Another facility discovers a visit and receives nothing. A care context was
linked with no records behind it.
- A bundle is refused at the far end, asynchronously, with no explanation
reaching the desk. It was validated before sending rather than before storing.
- A list of visits becomes slow to open. Bundles are being held in the list
rather than stored separately and read when needed.
## Asynchronous calls and callbacks, why a 200 means very little
### In plain words
Nothing goes participant to participant. Every request is addressed to the gateway, which forwards it. Two things follow.
- **You get an acknowledgement, not an answer.** In the [M3](/docs/hiecm/v3/getting-started/glossary#m3) consent flow the [HIU](/docs/hiecm/v3/getting-started/glossary#hiu) asks, the HIE-CM returns the consent request id on a callback, and the patient's decision comes back later. Each call's page in the [API reference](/docs/hiecm/v3/api) names the callback it produces.
- **You have to be reachable.** Half of [M2](/docs/hiecm/v3/getting-started/glossary#m2) is endpoints the gateway calls on your system.
One exception. In the health information flow the HIU supplies a data push URL, and the HIP encrypts the records and pushes them there. That URL may differ from the HIU's registered gateway URL, to improve privacy. The permission came through the gateway. The bytes do not.
Match each callback to the call that caused it by `response.requestId`, which carries the `REQUEST-ID` you sent. Callbacks do not arrive in the order you sent the requests, and the same one can arrive twice, so a repeat must change nothing. Each callback is described in [the API reference](/docs/hiecm/v3/api).
### What happens
Send the call with a fresh `REQUEST-ID`, treat the 202 as receipt only, and wait for the callback. Key the handler on `response.requestId`, answer it quickly, and make a second delivery of the same request id a no-op.
### How you know it worked
A callback reaches your URL whose `response.requestId` equals the `REQUEST-ID` you sent. Given three calls and two callbacks, the call whose request id has no callback is the one outstanding.
### When it goes wrong
Nothing arrives: work through [the callback never arrives](/docs/hiecm/v3/troubleshooting/callback-never-arrives). A retry is appended as a new event: key on the request id. The code waits on the response body for the result: it never comes there, so the integration hangs.
## Proving a callback really came from ABDM
### In plain words
Every callback in the specifications declares bearer authentication. The token
arrives in the `Authorization` header as `Bearer <token>`. Check three things
before your handler does any work:
1. **A bearer token is present.** Reject a callback without one, and log the
rejection.
2. **An answer answers a call you made.** Its `response.requestId` matches the
`REQUEST-ID` of a request you sent. A callback that answers nothing you sent
is not yours to act on.
3. **It is for a facility you serve.** `X-HIP-ID` or `X-HIU-ID` names one of
your own facilities. This is the only check that covers a callback that
answers nothing, such as a discovery request, a link init or a consent
notification. A bridge can receive callbacks for services that are not
yours: on a shared sandbox bridge, another organisation's consent
notifications and link callbacks have arrived at an integrator's URL.
Storing one keeps another organisation's patient data, and answering one
acts on its behalf.
The keys that verify the token's signature are not among the published gateway
calls. Confirm at onboarding how to verify the token, and meanwhile hold the
three checks above.
The signature inside a consent artefact is a different thing. It signs the
artefact's contents and proves the artefact was not altered. Checking the
delivery does not check the artefact, and checking the artefact does not check
the delivery.
### How you know it worked
A callback carrying a bearer token and a request id you sent is processed. The
same body with the `Authorization` header removed is rejected before your
handler reads the payload, and the rejection is logged. The second is the test
worth writing, because it is the only one that fails loudly when the check is
silently skipped.
### When it goes wrong
**The check is skipped under load.** A handler that checks inside a try block
and continues on failure is worse than one that never checked, because it reads
as safe. Fail closed.
**Nothing arrives at all**, which is a different problem. See
[the callback never arrives](/docs/hiecm/v3/troubleshooting/callback-never-arrives).
### What happens
In the handler, before parsing the body for action: require the `Authorization` header with a bearer token; require that `X-HIP-ID` or `X-HIU-ID` names one of your facilities; and, on a callback that answers a call, require that `response.requestId` matches a `REQUEST-ID` your system sent and has not already handled. Reject otherwise. Do not invent a signature check against a key source the specifications do not publish.
### How you know it worked
A test posts a valid callback body without `Authorization` and sees it rejected and logged before any handler work.
### When it goes wrong
Never fall back to processing a callback that failed a check while you investigate. A callback whose request id is unknown to you, or whose facility header names a facility you do not serve, is logged and dropped, not stored, answered or retried.
## Care contexts, how records are grouped so they can be found
### In plain words
Each care context contains only two pieces of information:
- **Reference ID.** A unique internal identifier assigned by the
[HRP](/docs/hiecm/v3/getting-started/glossary#hrp) (HMIS/LMIS). Used to link
and retrieve the associated health records.
- **Display Name.** A user-friendly description to help identify the group of
records. Must not include any sensitive or confidential information such as
test results or diagnoses. Example: "OPD records (X-Ray, Prescription) from
3rd March 2023".
### Recommended approach for structuring care contexts
To ensure clarity and usability, it is recommended to organise patient data as
follows:
- Create one care context per outpatient visit (OPD)
- Create one care context per inpatient admission (IPD)
This approach provides a clear and event-based grouping of health records.
### JSON structure of care contexts
```json
{
"patient": {
"referenceNumber": "TMH-PUID-001",
"display": "TMH records for Kiran Kumar",
"careContexts": [
{
"referenceNumber": "2375639",
"display": "OPD records for 03 Oct 2022"
}
]
}
}
```
### What happens
Give each care context a `referenceNumber` your own system resolves to the records behind it, and a `display` a person recognises months later. Group by encounter: one per outpatient visit, one per inpatient admission, not one per test or document.
### How you know it worked
Three tests in one visit are one care context. A display name reads like "OPD records for 03 Oct 2022" and carries no diagnosis or result.
### When it goes wrong
A diagnosis or result in `display` leaks clinical information into a system built never to hold it, visible to anyone who can list the patient's care contexts. One care context per record produces a list no person can navigate.
## How a health record is encrypted between HIP and HIU
### In plain words
The scheme is [Elliptic Curve Diffie-Hellman](/docs/hiecm/v3/getting-started/glossary#ecdh) key exchange on Curve25519, with AES-GCM for the payload and HKDF to derive the session key. Only the HIU holding valid consent can read the data, and the design gives perfect forward secrecy: key material compromised later does not expose data exchanged earlier.
### Who holds which key
| Key material | Generated by | Where it goes |
| --- | --- | --- |
| Short term private key, DHSK(U) | HIU | Never leaves the HIU |
| Short term public key, DHPK(U) | HIU | Sent with the request |
| Nonce, RAND(U), 32 bytes | HIU | Sent with the request |
| Short term private key, DHSK(P) | HIP | Never leaves the HIP |
| Short term public key, DHPK(P) | HIP | Sent with the encrypted data |
| Nonce, RAND(P), 32 bytes | HIP | Sent with the encrypted data |
| Shared key, DHK(U,P) | Computed independently by both | Never transmitted |
| Session key, SK(U,P), 256 bit AES-GCM | Derived independently by both | Never transmitted |
| Long term private key | HIP | Never leaves the HIP. Signs the encrypted payload. |
A new key pair per exchange is what buys forward secrecy.
### What the HIP does, step by step
Six steps, once consent has validated.
1. Generate a key pair, DHSK(P) and DHPK(P), in the group the HIU specified.
2. Generate a 32 byte random value, RAND(P).
3. Compute the shared key DHK(U,P) from the HIU's public key DHPK(U) and the HIP's own private key DHSK(P).
4. Derive the salt and IV by XOR of RAND(P) and RAND(U). The first 20 bytes are the salt for HKDF, the last 12 bytes the IV.
5. Compute a 256 bit AES-GCM session key SK(U,P) with HKDF-SHA256, from the x coordinate of the shared key and that salt. The HKDF `info` is empty.
6. Encrypt the data with AES-256-GCM, that key and that IV, with no additional authenticated data. Append the 16 byte authentication tag to the ciphertext and base64 encode the result.
The HIP then sends DHPK(P), RAND(P) and the encrypted data. The HIU derives the same session key from its own private key DHSK(U) and the HIP's public key DHPK(P), with salt and IV from the same XOR.
Build the shared key from the HIU's public key and the HIP's private key. That is the pairing that makes the Diffie-Hellman exchange work.
### Do not write this yourself
Two reference implementations exist. Fidelius, at [github.com/sukreet/fidelius](https://github.com/sukreet/fidelius), and the Fidelius CLI, which is Java, with worked examples for Node.js, Python, Ruby and PHP at [github.com/mgrmtech/fidelius-cli](https://github.com/mgrmtech/fidelius-cli/tree/main/examples) that run the binary as a subprocess. A webinar covers the CLI from both sides, at [youtu.be/rSir2gbkEmk](https://youtu.be/rSir2gbkEmk?t=9232) from 2:33:52.
### What happens
Generate a fresh key pair and nonce per transfer. XOR the two nonces: the first 20 bytes are the HKDF salt, the last 12 the IV. Derive the key with HKDF-SHA256 over the x coordinate of the ECDH result, with an empty `info`, for 32 bytes. Encrypt with AES-256-GCM, no additional authenticated data, and append the 16 byte tag before base64 encoding.
### How you know it worked
The HIU decrypts every entry and its tag check passes. Before any peer is involved, round trip a record through the Fidelius CLI and compare bytes.
### When it goes wrong
Every tag check fails: compare the salt and IV split, the HKDF input (the x coordinate, not the whole shared secret) and an `info` or additional data that should be empty. One HIP fails while others work: compare how its public key is encoded.
## Reading an ABDM error code
### In plain words
ABDM returns [several different error shapes](/docs/hiecm/v3/api/m1/errors), and
only some of them carry a code. Parse for all of them before you write any handling,
because the shape tells you where the failure came from.
Every code in the [error code reference](/docs/hiecm/v3/reference/error-codes)
carries an action. Key your handling to that column rather than to a list of
codes you maintain by hand.
| Action | What your code does |
| --- | --- |
| Fix request | Do not retry. Something you sent is wrong, and sending it again will not help |
| Fix auth | Fetch a fresh token, then retry once |
| New request id | Generate a new `REQUEST-ID`, then retry once |
| Retry | Back off and retry, with a ceiling on attempts |
| Cannot proceed | Stop, and tell the person why in their own terms |
| Ask support | Stop, and collect the ids before the context is lost |
| Unclassified | Treat 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](/docs/hiecm/v3/troubleshooting).
Read the code in the body before the HTTP status. A 404 can carry `ABDM-1016`,
Invalid Timestamp: the route exists and the request was refused. A 404 whose
body carries `Status report` and no code means no route matched the request. A
code can arrive bare or with a trailing colon and space, as in `ABDM-1016: `, so
match on the code itself.
### What happens
Parse the body for an error code before acting on the status. Strip a trailing colon and space from the code, then key the handling on the action column of the [error code reference](/docs/hiecm/v3/reference/error-codes), not on a list maintained by hand.
### How you know it worked
Given a code, you can say what it means and which header or field it concerns: `ABDM-2403` is Invalid X-CM-ID, the consent manager header.
### When it goes wrong
A one line message is read as a diagnosis: Invalid header covers many causes. A code that is not in the reference is handled as Unclassified, not guessed at. A 404 is treated as a wrong path when its body carries a code.
## Roles, which entity your software acts for and which way the record moves
### In plain words
There are two integrator roles on HIE-CM, and your product is one of them for its
whole life. What decides it is which entity your software acts for.
| Role | It acts for | What you build |
| --- | --- | --- |
| [IMS](/docs/hiecm/v3/getting-started/glossary#ims) | A care provider. An HMIS in a hospital, an EMR in a clinic, a LIMS in a laboratory, a PMS in a pharmacy | [M1](/docs/hiecm/v3/milestones/m1) to [M4](/docs/hiecm/v3/milestones/m4) |
| [PHR](/docs/hiecm/v3/getting-started/glossary#phr) | A care seeker, who holds their own records and gives consent | [P1](/docs/hiecm/v3/milestones/p1) to [P3](/docs/hiecm/v3/milestones/p3) |
[HIP](/docs/hiecm/v3/getting-started/glossary#hip) and
[HIU](/docs/hiecm/v3/getting-started/glossary#hiu) are not a third and a fourth
role, and they are not something you register as. They are the two ends of one
record moving: whoever publishes it is the HIP for that exchange, and whoever
asks to read one they did not create is the HIU.
Both roles are both, and it changes call by call:
- A hospital is the HIP when it shares a discharge summary, and the HIU when it pulls an earlier prescription, through the same IMS.
- A citizen is the HIP when they push a record from their PHR application, and the HIU when they fetch one.
Neither is a thing you can build once and be. See
[HIP and HIU](/docs/hiecm/v3/concepts/hip-hiu).
So the milestones you build follow the direction your records move, not the kind
of product you sell. A PHR app that lets a citizen push a record publishes as
the HIP, and builds the M2 linking and transfer calls as well. See
[where the citizen is the HIP](/docs/hiecm/v3/milestones/p2#where-the-citizen-is-the-hip).
Records never pass through the consent manager: it routes the request and holds
the consent, and the record goes from the system that holds it to the system
that asked.
### What happens
Decide the role once, IMS or PHR, from the entity. Then list every direction a record moves through the product: publishing a record is HIP behaviour, fetching one it did not create is HIU behaviour. Build the milestones for each direction the product uses.
### How you know it worked
For a hospital system that also pulls a patient's history, you can name the role, IMS, both directions, and the milestones: M1, M2 and M3. For a PHR app that uploads a scanned prescription, you can say the citizen is the HIP for that record.
### When it goes wrong
HIP, HIU, health repository and health locker are chosen as though they were one list of company types: two are directions, one is custody, one is a product. A PHR app is built for P1 alone and then cannot publish the first record a citizen pushes. A fetch is designed against the consent manager, which holds no records.
## Where these came from
- `hiecm.concept.m2-exchange-not-call`
- `hiecm.concept.m2-never-block-the-desk`
- `hiecm.concept.m2-retry-is-a-duplicate`
- `hiecm.concept.m2-integrator-call-panel`
- `hiecm.concept.m2-inbound-is-a-surface`
- `hiecm.concept.m2-care-context-and-records`
- `hiecm.concept.asynchronous-callbacks`
- `hiecm.concept.callback-authenticity`
- `hiecm.concept.care-context`
- `hiecm.concept.data-flow-encryption`
- `hiecm.concept.error-codes`
- `hiecm.concept.roles`

View File

@ -0,0 +1,70 @@
# Integrate M2, create and link records
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
24 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/api/hiecm/consent/v3/request/hip/on-notify` | Acknowledge the notification sent when a consent request is approved/revoked/ex… |
| `POST` | `/api/hiecm/data-flow/v3/health-information/hip/on-request` | Health information data request acknowledgement from HIP. |
| `POST` | `/api/hiecm/data-flow/v3/health-information/notify` | Notifications corresponding to events during data flow |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/hiecm/hip/v3/link/carecontext` | Perform HIP initiated linking. |
| `POST` | `/api/hiecm/hip/v3/link/context/notify` | Notify CM about any update on the already linked care context for a patient. |
| `POST` | `/api/hiecm/hip/v3/link/patient/links/sms/notify2` | Send SMS notification to patient that a care context is linked. |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/on-confirm` | Sharing the response of /api/hiecm/user-initiated-linking/v3/link/care-context/… |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/on-init` | As a result of the initialization, HIP has to generate a unique reference-numbe… |
| `POST` | `/api/hiecm/user-initiated-linking/v3/patient/care-context/on-discover` | HMIS/LIMS/HIP has to ensure that only unlinked records of the patient has to be… |
| `POST` | `/api/hiecm/v3/token/generate-token` | Generate link token to link the health records |
| `POST` | `/api/v3/consent/request/hip/notify` | This is a callback API to notify HIP when consent is APPROVED or REVOKED. |
| `POST` | `/api/v3/hip/health-information/request` | Health information data request to HIP. |
| `POST` | `/api/v3/hip/link/care-context/confirm` | Confirm the linking of care contexts for a patient. It allows healthcare inform… |
| `POST` | `/api/v3/hip/link/care-context/init` | Initiate the linking of care contexts for a patient. It allows healthcare infor… |
| `POST` | `/api/v3/hip/patient/care-context/discover` | Discover care contexts associated with a patient. It allows healthcare informat… |
| `POST` | `/api/v3/hip/token/on-generate-token` | This is a call back API of [/api/hiecm/v3/token/generate-token]. |
| `POST` | `/api/v3/link/on_carecontext` | Is a callback API that will be called by HIE-CM. The response will be received … |
| `POST` | `/api/v3/links/context/on-notify` | This API endpoint is a call back API for /api/hiecm/hip/v3/link/context/notify … |
| `POST` | `/api/v3/patients/sms/on-notify` | This API endpoint is a call back API for /api/hiecm/hip/v3/link/patient/links/s… |
| `POST` | `/health-information/transfer` | health information transfer API |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIP-ID` | Identifier of the health information provider to which the request was intended |
| `X-LINK-TOKEN` | JWT Authentication token which was issued by ABDM after successful validation of username and password |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/hip/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"consentId": "e3c74829-3f82-4f94-959e-e10f57bcd57b"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```

View File

@ -0,0 +1,562 @@
# HIE-CM m2 build
Scaffolds an ABDM m2 integration one journey at a time. It covers care contexts, HIP initiated linking, discovery, and pushing encrypted records to a requester.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Link token (`m2-abdm-link-token-hip`)
**Act: the calls in this journey, in order**
#### 1. Generate link token to link the health records (`m2_post_v3_token_generate_token`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/v3/token/generate-token \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"abhaNumber": 12345678901234,
"abhaAddress": "<ABHA_ADDRESS>",
"name": "first_name + middle_name + last_name",
"gender": "M",
"yearOfBirth": 9999
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/v3/token/generate-token` is answered by `POST /api/v3/hip/token/on-generate-token` on your bridge.
### HIP initiated linking (`m2-abdm-hip-initiated-linking-hip`)
**Act: the calls in this journey, in order**
#### 1. Perform HIP initiated linking (`m2_post_hip_v3_link_carecontext`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/hip/v3/link/carecontext \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'X-LINK-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"abhaNumber": 12345678901234,
"abhaAddress": "<ABHA_ADDRESS>",
"patient": [
{
"referenceNumber": "TMH-PUID-001",
"display": "String",
"careContexts": [
{
"referenceNumber": "TMH-PUID-001",
"display": "display 1"
}
],
"hiType": "DiagnosticReport",
"count": 1
}
]
}'
```
#### 2. Notify a change to a linked care context (`m2_post_hip_v3_link_context_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/hip/v3/link/context/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"patient": {
"id": "<ABHA_ADDRESS>"
},
"careContext": {
"patientReference": "<ABHA_ADDRESS>",
"careContextReference": "b009a970-8b04-4779-abd1-b50f113245bf"
},
"hiTypes": [
"DiagnosticReport"
],
"date": "2024-05-09T10:34:00.387Z",
"hip": {
"id": "ABDM_HIP"
}
}
}'
```
#### 3. Send SMS notification to patient that a care context is linked (`m2_post_hip_v3_link_patient_links_sms_notify2`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/hip/v3/link/patient/links/sms/notify2 \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"phoneNo": "986543***",
"hip": {
"id": "ABDM_HIP",
"name": "ABC Hospital"
}
}
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/hip/v3/link/carecontext` is answered by `POST /api/v3/link/on_carecontext` on your bridge.
- `POST /api/hiecm/hip/v3/link/context/notify` is answered by `POST /api/v3/links/context/on-notify` on your bridge.
- `POST /api/hiecm/hip/v3/link/patient/links/sms/notify2` is answered by `POST /api/v3/patients/sms/on-notify` on your bridge.
### User initiated linking (`m2-abdm-user-initiated-linking-hip`)
**Act: the calls in this journey, in order**
#### 1. Answer the care context discovery (`m2_post_user_initiated_linking_v3_patient_care_context_on_8c9340`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/user-initiated-linking/v3/patient/care-context/on-discover \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"transactionId": "f901b782-bfdf-4224-9f8d-da2cadc20c0d",
"patient": [
{
"referenceNumber": "<ABHA_ADDRESS>",
"display": "12345",
"careContexts": [
{
"referenceNumber": "abc123",
"display": "12345"
}
],
"hiType": "Prescription",
"count": 1
}
],
"matchedBy": [
"MR"
],
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
#### 2. Link care context on init (`m2_post_user_initiated_linking_v3_link_care_context_on_init`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/user-initiated-linking/v3/link/care-context/on-init \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"transactionId": "f901b782-bfdf-4224-9f8d-da2cadc20c0d",
"link": {
"referenceNumber": "d353b782-bfdf-4224-9f8d-da2cadc20c0d",
"authenticationType": "DIRECT",
"meta": {
"communicationMedium": "MOBILE",
"communicationHint": "OTP",
"communicationExpiry": "2024-05-01T05:22:34.123Z"
}
},
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
#### 3. Link care context on confirm (`m2_post_user_initiated_linking_v3_link_care_context_on_confirm`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/user-initiated-linking/v3/link/care-context/on-confirm \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"patient": [
{
"referenceNumber": "<ABHA_ADDRESS>",
"display": "12345",
"careContexts": [
{
"referenceNumber": "abc123",
"display": "12345"
}
],
"hiType": "Prescription",
"count": 1
}
],
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
**Exit condition (Observe until this is true)**
A 202 on this reply, sent with `response.requestId` set to the `REQUEST-ID` of the `POST /api/v3/hip/link/care-context/confirm` request it answers. A reply without that id answers nothing.
### Consent and data flow (`m2-consent-management-data-flow-hip`)
**Act: the calls in this journey, in order**
#### 1. Acknowledge the consent notification (`m2_post_consent_v3_request_hip_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/hip/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"consentId": "e3c74829-3f82-4f94-959e-e10f57bcd57b"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
#### 2. Submit the health information data request acknowledgement from HIP (`m2_post_data_flow_v3_health_information_hip_on_request`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/hip/on-request \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"hiRequest": {
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"sessionStatus": "ACKNOWLEDGED"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
#### 3. Submit the notifications corresponding to events during data flow (`m2_post_data_flow_v3_health_information_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"consentId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"doneAt": "2023-01-24T06:35:44.167Z",
"notifier": {
"type": "HIP",
"id": "IN2810014366"
},
"statusNotification": {
"sessionStatus": "TRANSFERRED",
"hipId": "IN2810014366",
"statusResponses": [
{
"careContextReference": "10004-20200001768-1",
"hiStatus": "DELIVERED",
"description": "Data sent successfully"
}
]
}
}
}'
```
**Exit condition (Observe until this is true)**
A 202 response. The specification gives no body for it, so read what comes back.
### Callbacks (`m2-callbacks`)
**Act: the calls in this journey, in order**
#### 1. Receive the HIP token on generate token (`m2_post_v3_hip_token_on_generate_token`)
Inbound to your bridge at `/api/v3/hip/token/on-generate-token`. Acknowledge it and continue.
#### 2. Link on carecontext (`m2_post_v3_link_on_carecontext`)
Inbound to your bridge at `/api/v3/link/on_carecontext`. Acknowledge it and continue.
#### 3. Receive the links context on notify (`m2_post_v3_links_context_on_notify`)
Inbound to your bridge at `/api/v3/links/context/on-notify`. Acknowledge it and continue.
#### 4. Receive the patients SMS on notify (`m2_post_v3_patients_sms_on_notify`)
Inbound to your bridge at `/api/v3/patients/sms/on-notify`. Acknowledge it and continue.
#### 5. Discover care contexts associated with a patient (`m2_post_v3_hip_patient_care_context_discover`)
Inbound to your bridge at `/api/v3/hip/patient/care-context/discover`. Acknowledge it and continue.
#### 6. Initiate the linking of care contexts for a patient (`m2_post_v3_hip_link_care_context_init`)
Inbound to your bridge at `/api/v3/hip/link/care-context/init`. Acknowledge it and continue.
#### 7. Confirm the linking of care contexts for a patient (`m2_post_v3_hip_link_care_context_confirm`)
Inbound to your bridge at `/api/v3/hip/link/care-context/confirm`. Acknowledge it and continue.
#### 8. Receive the consent decision (`m2_post_v3_consent_request_hip_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/v3/consent/request/hip/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"status": "GRANTED",
"consentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"consentDetail": {
"schemaVersion": "v3",
"consentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"createdAt": "2024-05-01T05:10:20.123Z",
"patient": {
"id": "<ABHA_ADDRESS>"
},
"careContexts": [
{
"patientReference": "batman@tmh",
"careContextReference": "Episode1"
}
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "www.abc.com"
},
"hip": {
"id": "cowin_hip_01",
"name": "Cowin",
"type": "HIP"
},
"hiu": {
"id": "cowin_hiu_01",
"name": "Cowin",
"type": "HIU"
},
"consentManager": {
"id": "abdm"
},
"requester": {
"name": "<ABHA_ADDRESS>",
"identifier": {
"value": "REG1",
"type": "MH1001",
"system": "https://www.sample.com"
}
},
"hiTypes": [
"Prescription"
],
"permission": {
"accessMode": "VIEW",
"dateRange": {
"from": "2021-09-28T12:30:08.573Z",
"to": "2021-09-28T12:30:08.573Z"
},
"dataEraseAt": "2021-09-28T12:30:08.573Z",
"frequency": {
"unit": "HOUR",
"value": 1,
"repeats": 0
}
}
},
"signature": "e8nY601CYDsC0FKoDjSp+7GeQ2s2R8oZncLCz5ce+pEuDOr5bZV0aaHjwJg4b9S9V+twjt4hbojx3fl7egrt8+0c+lfPTi5/bBUAQXCABTfFmtFU7jn65HlTt8kgkiONx26ZBhJ0wX3xjYI72PPtzYIiT5Q08YtDoILA62KceioV7lwuKssw7wC4ECbBAvRuXT121TmtrPhf+0myJATSnaajS06S6OthrKfZLNTUFf3pFiJzqouSTrjNblOX6DT2+JuO3rom1Szz/03c0HQG+wWASv+PO3J6uRs0UI4JvKmM/4tP+Z+/HPKM15K5U5K+4pqf6czKrbIDpkT/kP8bGg==",
"grantAcknowledgement": false
}
}'
```
#### 9. Receive the health information data request to HIP (`m2_post_v3_hip_health_information_request`)
Inbound to your bridge at `/api/v3/hip/health-information/request`. Acknowledge it and continue.
#### 10. Push the encrypted records to the HIU's data push URL (`m2_post_health_information_transfer`)
Outbound from your bridge. POST each page of encrypted records to the `dataPushUrl` in the health information request of step 9. The call goes straight to the HIU, not through the gateway. The HIU answers 202 for each page.
**Exit condition (Observe until this is true)**
A 202 response. The specification gives no body for it, so read what comes back.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/m2
- Error codes: /docs/hiecm/v3/api/m2/errors

View File

@ -0,0 +1,91 @@
---
name: abdm-m3
description: Use when building, debugging or testing ABDM Milestone 3: raising a consent request, tracking its status, reading consent artefacts, and fetching encrypted health records as an HIU. Carries the endpoints and the consent rules. Also carries the scaffolding loop that builds it flow by flow and the loop from a failed call to a named fix, in references/.
type: skill
domain: m3
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- hiu-registration
- callback-url
- abha-address
produces:
- consent-request-id
- consent-artefact
can_execute: true
can_orchestrate: false
---
# ABDM M3, fetch data with consent
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m3/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M3
- Ask a patient, through their consent manager, for records another provider holds.
- Track that request through a grant, a denial, a revocation and an expiry.
- Receive the encrypted records at your data push URL and decrypt them.
- Handle one request that produces several consent artefacts.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Raise a consent request and fetch the records it covers"
- "What has to be in place before my first M3 call?"
- "My M3 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 16 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- One consent request produces one artefact per HIP, so store the request id and every artefact id. Taking the first element is the bug that silently drops half a fetch.
- You act as the HIU. The HIE-CM holds the consent and asks the patient on your behalf. No artefact, no records.
- The patient must be known to you by ABHA address before you can raise a request.
- Records arrive encrypted at the `dataPushUrl` the health information request names. Decrypt them with the key material that request carries.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m3
- The flows as diagrams: /docs/hiecm/v3/milestones/m3
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug M3, fetch data with consent
The codes this module can return, with the message and the operation that returns each.
## Errors
The M3, fetch data with consent specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,156 @@
# Design M3, fetch data with consent
What the integration has to do to the journey around the calls: how many questions a patient is asked, where a failure is shown, and what a screen is forbidden to claim. Every rule below comes from a Catalogue atom, cited at the end.
## Read plural as plural, and never translate a code into a meaning
### In plain words
Two habits cause most of the avoidable failures on the consent side, and both
are about reading what came back rather than what you expected.
A consent request produces one artefact per HIP, so a request that spans
facilities returns several. Taking the first element is
the bug that silently drops half a fetch.
And the same error code means different things in different modules, so a code
alone is not a meaning.
### What happens
Hold the collection. A length of one is a case, not the normal case. It is worth
having a function whose existence prevents any call site from reaching for the
first element, because the failure is silent: half the records simply never
arrive, and nothing reports an error.
Carry ABDM's message through to the screen and never translate a code into words
of your own. `ABDM-1112` is an unusable consent in one place and a DigiLocker
gender mismatch in a module error table. `ABDM-1062` is a withheld consent in
one and a link token mismatch in another. Whichever you choose to display, you
will be wrong for the other module, and the message ABDM sent is the only part
that names the actual cause.
Handle two answers from consent status and consent fetch. The specification
documents them as synchronous responses carrying a status and an artefact
array. One run against ids that did not exist returned `202` with an empty
body instead. That single observation is not a rule: check the status before
you read the body, treat a `200` as the documented answer, treat a `202` with
no body as an acknowledgement whose answer arrives on the callback, and confirm
which you get on your own data before building on either.
### How you know it worked
Raise a consent covering care contexts at two HIPs. The number of artefacts you
hold matches the number ABDM returned, and a fetch runs for each one.
Trigger a refusal. The screen shows ABDM's own message text alongside the code,
and no wording of your own has replaced it.
### When it goes wrong
- A fetch returns fewer records than the consent covers, and nothing errors.
Something took the first artefact and discarded the rest.
- A displayed explanation contradicts what ABDM sent. A code was translated
locally instead of the message being carried through.
- A `202` with an empty body is read as a failure, or a `200` with a body is
parsed as if it were empty. The status decides which answer you hold.
## Refuse to guess an undocumented step, and say so before the work starts
### In plain words
M3's data transfer needs a key derivation and a symmetric cipher over the shared
secret. Neither is in the specification. The
[data flow page](/docs/hiecm/v3/concepts/data-flow) and the
Fidelius reference give the
scheme: HKDF over the shared secret, and AES-GCM for the payload. Build the step
from those. A desk that reads only the specification cannot complete it, and no
amount of care in the code around it changes that.
What matters is where the integrator finds out. A desk that discovers it cannot
decrypt at the moment records arrive has discovered it in the worst place
available: asynchronously, after a consent has been served, with a patient's
records in hand and nothing to do with them.
### What happens
Build every step from what is documented. Where a step is documented nowhere,
throw on it with a message naming exactly what is missing. Not a generic
failure. The name of the step, and what would have to be published for it to
work.
Then report it in the readiness check up front, rather than at the moment the
first encrypted bundle arrives.
The rule generalises past encryption. Where a capability cannot work, say so
before somebody depends on it, in the place they would act on it. A capability
that is going to fail should fail in the readiness check, where an integrator is
already looking for problems, not in a flow where they are looking for records.
Do not add a dependency to paper over a scheme nobody has written down. A guess
with a package name on it is still a guess, and it is harder to find later
because it looks like a decision somebody made deliberately.
### How you know it worked
Open the readiness check before running anything. It names any step that is
documented nowhere as unavailable, and says which part is missing.
Run the flow anyway. Where a step is missing, it fails at that step with a
message naming it, rather than at a later point with a decoding error.
### When it goes wrong
- Records arrive and cannot be read, and the failure reads as a corrupt payload.
The undocumented step failed quietly somewhere earlier.
- A library appeared in the dependency list to solve the cipher, chosen from a
sample rather than from the data flow page. Check it implements HKDF and
AES-GCM as that page gives them.
- The readiness check is green and the capability does not work. The check is
testing configuration rather than capability.
## Consent, what it authorises and how it ends
### In plain words
There are five states, in the two sections a PHR app shows: Requests holds Requested, Denied and Expired; Approved holds Granted and Revoked.
```mermaid
stateDiagram-v2
[*] --> Requested: HIU raises a consent request
Requested --> Granted: Patient approves
Requested --> Denied: Patient refuses
Requested --> Expired: Patient does not act in time
Granted --> Revoked: Patient withdraws access
Granted --> [*]: Validity period ends
```
| State | What it means | What your system does |
| --- | --- | --- |
| Requested | The patient has not acted yet. | Wait. Poll the request status if you need to show progress. |
| Granted | The patient approved, and set how long the access lasts. | Fetch the artefact ids, then request the data. |
| Denied | The patient refused. | Stop. There is no partial result and no retry that changes the answer. |
| Expired | The patient did not act inside the window the HIU set on the request. | Raise a new request if the clinical need is still there. |
| Revoked | The patient withdrew a consent they had already granted. | Stop fetching under that artefact from that moment. |
Two clocks run here. The **request window** is how long the patient has to answer, set by the HIU, and running out produces Expired. The **consent validity period** is how long access lasts once granted, set by the patient as they grant, with a defined expiry date and time. Neither is the **date range**, which says which records are in scope by when the care happened: a consent granted today can cover records from 2019.
Granted is not permanent. Revoked and Expired are ordinary destinations, not faults, and your system will meet both in production.
### What happens
Store the consent request id from the init callback, then every consent artefact id a grant returns. Before each fetch, check the artefact is still usable: the patient can revoke at any time, and the validity period ends on its own.
### How you know it worked
A granted request returns at least one artefact id, and a fetch under it is accepted while the artefact is within its validity period and not revoked.
### When it goes wrong
A fetch that worked last week fails today because the patient revoked or the validity period ended; that is the system working. `ABDM-1062` is consent not granted, and `ABDM-1112` is an artefact id that is invalid or already expired. Records kept past the consent window are a legal problem, not a technical one.
## Where these came from
- `hiecm.concept.m3-plural-artefacts-and-codes`
- `hiecm.concept.m3-refuse-to-guess`
- `hiecm.concept.consent-artefact`

View File

@ -0,0 +1,55 @@
# Integrate M3, fetch data with consent
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
16 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/api/hiecm/consent/v3/fetch` | Fetch the consent details |
| `POST` | `/api/hiecm/consent/v3/request/hiu/on-notify` | Acknowledge the notification sent when a consent request is approved/denied/rev… |
| `POST` | `/api/hiecm/consent/v3/request/init` | Initiate the consent request |
| `POST` | `/api/hiecm/consent/v3/request/status` | Get consent request status |
| `POST` | `/api/hiecm/data-flow/v3/health-information/notify` | Notifications corresponding to events during data flow |
| `POST` | `/api/hiecm/data-flow/v3/health-information/request` | Health information data request from HIU. |
| `GET` | `/api/hiecm/data-flow/v3/health-information/request/status/{transaction-id}` | Get the current status of the Health Information Request. |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/v3/hiu/consent/on-fetch` | This is a callback API called by CM to provide fetched consent artefact details… |
| `POST` | `/api/v3/hiu/consent/request/notify` | This is a callback API to notify HIU when consent is APPROVED, DENIED or REVOKE… |
| `POST` | `/api/v3/hiu/consent/request/on-init` | Callback API of consent request for patient HIU. |
| `POST` | `/api/v3/hiu/consent/request/on-status` | Callback API of consent status request. |
| `POST` | `/api/v3/hiu/health-information/on-request` | Health information data request acknowledgement to HIU. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/fetch \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"consentId": "5f7a535d-a3fd-416b-b069-c97d021fbacd"
}'
```

View File

@ -0,0 +1,377 @@
# HIE-CM m3 build
Scaffolds an ABDM m3 integration one journey at a time. It covers raising a consent request, tracking it, and fetching the records it covers as an HIU.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Consent and data flow (`m3-consent-management-data-flow-hiu`)
**Act: the calls in this journey, in order**
#### 1. Initiate the consent request (`m3_post_consent_v3_request_init`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/init \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"consent": {
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "www.abc.com"
},
"patient": {
"id": "<ABHA_ADDRESS>"
},
"hip": null,
"hiu": {
"id": "cowin_hiu_01"
},
"careContexts": [
{
"patientReference": "batman@tmh",
"careContextReference": "Episode1"
}
],
"requester": {
"name": "<ABHA_ADDRESS>",
"identifier": {
"value": "REG1",
"type": "MH1001",
"system": "https://www.sample.com"
}
},
"hiTypes": [
"Prescription"
],
"permission": {
"accessMode": "VIEW",
"dateRange": {
"from": "2021-09-28T12:30:08.573Z",
"to": "2021-09-28T12:30:08.573Z"
},
"dataEraseAt": "2021-09-28T12:30:08.573Z",
"frequency": {
"unit": "HOUR",
"value": 1,
"repeats": 0
}
}
}
}'
```
#### 2. Get consent request status (`m3_post_consent_v3_request_status`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/status \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"consentRequestId": "5f7a535d-a3fd-416b-b069-c97d021fbacd"
}'
```
#### 3. Acknowledge the consent notification (`m3_post_consent_v3_request_hiu_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/hiu/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": [
{
"status": "OK",
"consentId": "e3c74829-3f82-4f94-959e-e10f57bcd57b"
}
],
"error": {
"code": "ABDM-1001",
"message": "unable to connect database"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
#### 4. Fetch the consent details (`m3_post_consent_v3_fetch`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/fetch \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"consentId": "5f7a535d-a3fd-416b-b069-c97d021fbacd"
}'
```
#### 5. Submit the health information data request from HIU (`m3_post_data_flow_v3_health_information_request`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/request \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"hiRequest": {
"consent": {
"id": "18235d89-cb13-479d-ad71-7a57d5f669a8"
},
"dateRange": {
"from": "2022-10-06T15:10:00.587Z",
"to": "2022-11-06T15:10:00.587Z"
},
"dataPushUrl": "https://live.ndhm.gov.in/api-hiu/data/notification",
"keyMaterial": {
"cryptoAlg": "ECDH",
"curve": "curve25519",
"dhPublicKey": {
"expiry": "2022-12-28T13:18:20.742Z",
"parameters": "Ephemeral public key",
"keyValue": "BFN7KTdOT0jIAExG2A8Jg+01wMPWxptiGqwHRVvtiVEsUq2FR7P2UdqZxJyPJSeR6muai21iQhasNxnhh8I5M+g="
},
"nonce": "28236d89-cb13-479d-ad71-7a57d5f669a9"
}
}
}'
```
#### 6. Submit the notifications corresponding to events during data flow (`m3_post_data_flow_v3_health_information_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"consentId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"doneAt": "2023-01-24T06:35:44.167Z",
"notifier": {
"type": "HIU",
"id": "100005"
},
"statusNotification": {
"sessionStatus": "RECEIVED",
"hipId": "IN2810014366",
"statusResponses": [
{
"careContextReference": "10004-20200001768-1",
"hiStatus": "OK",
"description": "Data received successfully"
}
]
}
}
}'
```
#### 7. Get the current status of the health information request (`m3_get_data_flow_v3_health_information_request_status_tra_550104`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/request/status/{transaction-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/consent/v3/request/init` is answered by `POST /api/v3/hiu/consent/request/on-init` on your bridge.
- `POST /api/hiecm/consent/v3/request/status` is answered by `POST /api/v3/hiu/consent/request/on-status` on your bridge.
- `POST /api/hiecm/consent/v3/fetch` is answered by `POST /api/v3/hiu/consent/on-fetch` on your bridge.
- `POST /api/hiecm/data-flow/v3/health-information/request` is answered by `POST /api/v3/hiu/health-information/on-request` on your bridge.
Then `GET /api/hiecm/data-flow/v3/health-information/request/status/{transaction-id}` returns a 200 whose body matches:
```json
{
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"status": "TRANSFERRED"
}
```
### Callbacks (`m3-callbacks`)
**Act: the calls in this journey, in order**
#### 1. Receive the consent request for patient HIU (`m3_post_v3_hiu_consent_request_on_init`)
Inbound to your bridge at `/api/v3/hiu/consent/request/on-init`. Acknowledge it and continue.
#### 2. Receive the consent status request (`m3_post_v3_hiu_consent_request_on_status`)
Inbound to your bridge at `/api/v3/hiu/consent/request/on-status`. Acknowledge it and continue.
#### 3. Notify HIU when consent is APPROVED, DENIED or REVOKED (`m3_post_v3_hiu_consent_request_notify`)
Inbound to your bridge at `/api/v3/hiu/consent/request/notify`. Acknowledge it and continue.
#### 4. Receive the provide fetched consent artefact details to HIU (`m3_post_v3_hiu_consent_on_fetch`)
Inbound to your bridge at `/api/v3/hiu/consent/on-fetch`. Acknowledge it and continue.
#### 5. Receive the health information data request acknowledgement to HIU (`m3_post_v3_hiu_health_information_on_request`)
Inbound to your bridge at `/api/v3/hiu/health-information/on-request`. Acknowledge it and continue.
**Exit condition (Observe until this is true)**
A 200 response. The specification gives no body for it, so read what comes back.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/m3
- Error codes: /docs/hiecm/v3/api/m3/errors

View File

@ -0,0 +1,88 @@
---
name: abdm-m4
description: Use when building, debugging or testing ABDM Milestone 4, the NHPR: creating an HPID, registering a healthcare professional on the HPR, onboarding a facility to the HFR, and linking that facility to a bridge. Carries the operations, their hosts and headers.
type: skill
domain: m4
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
produces:
- hpid
- facility-id
- bridge-facility-link
can_execute: true
can_orchestrate: false
---
# ABDM M4, register facilities and professionals
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m4/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M4
- Create an HPID for a professional and register them in the HPR.
- Register a facility in the HFR and carry it through to submission.
- Link a facility to a bridge and set the type of each link.
- Fetch the council, course, college, university and geography codes these calls take instead of names.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Onboard this facility to the HFR and link its HIP bridge"
- "What has to be in place before my first M4 call?"
- "My M4 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 91 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- Neither registry moves a health record. M4 establishes who the professional is and what the facility is, so every record flow has a verified provider behind it.
- The register professional call carries an `hprToken` in its payload, beside the practitioner.
- The facility calls are keyed to a `trackingId`, and the facility status values in the examples include `Draft` and `Submitted`. Read the status rather than assuming a facility is complete.
- Register professional takes codes, not names. Fetch council, course, college, university, state, district and language from the master data APIs first.
- `POST /v1/bridges/MutipleHRPAddUpdateServices` links a facility to a bridge. Each entry names a `bridgeId`, a `hipName`, a `type` such as `HIP`, and whether it is `active`.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m4
- The flows as diagrams: /docs/hiecm/v3/milestones/m4
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug M4, register facilities and professionals
The codes this module can return, with the message and the operation that returns each.
## Errors
The M4, register facilities and professionals specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,133 @@
# Integrate M4, register facilities and professionals
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://apihspsbx.abdm.gov.in/v4/int` NHPR, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
91 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/aadhaar/generateLink` | Generate Aadhaar Link |
| `POST` | `/aadhaar/isAuthenticated` | Is Aadhaar Authenticated |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/v1/auth/authPassword` | Login Via Password |
| `GET` | `/api/v1/auth/cert` | Get Public Certificate |
| `POST` | `/api/v1/auth/confirmWithAadhaarOtp` | Verify Aadhaar OTP |
| `POST` | `/api/v1/auth/init` | Send Via Aadhaar OTP |
| `POST` | `/api/v2/auth/loginViaMobileSendOTP` | Send Verify OTP |
| `POST` | `/apis/v1/doctors/fetch-documents-list` | Fetch Documents |
| `POST` | `/apis/v1/doctors/fetch-professional-info` | Get Professional Info |
| `POST` | `/apis/v1/doctors/generate-mobile-otp` | Generate Mobile OTP 2 |
| `POST` | `/apis/v1/doctors/generate-verification-email` | Send Verification Email |
| `POST` | `/apis/v1/doctors/regenerate-mobile-otp` | Regenerate Mobile OTP |
| `POST` | `/apis/v1/doctors/register-professional-new` | Register Professional |
| `POST` | `/apis/v1/doctors/resent-verify-email` | Resend Verification Email |
| `POST` | `/apis/v1/doctors/update-professional-new` | Update Professional |
| `POST` | `/apis/v1/doctors/verify-email-otp` | Verify Email OTP |
| `POST` | `/apis/v1/doctors/verify-mobile-otp` | Verify Mobile OTP |
| `GET` | `/apis/v1/masters/affiliated-board` | Get All Affiliated Board |
| `GET` | `/apis/v1/masters/affiliated-board/{id}` | Get Affiliated Board By ID |
| `GET` | `/apis/v1/masters/affiliated-board/states/{id}` | Get Affiliated Board By State ID |
| `GET` | `/apis/v1/masters/colleges/{id}` | Get College By State |
| `GET` | `/apis/v1/masters/colleges/{stateId}/{medicineId}` | Get College By State And Medicine ID |
| `GET` | `/apis/v1/masters/countries` | Get All Countries |
| `GET` | `/apis/v1/masters/countries/{id}` | Get Countries By ID |
| `POST` | `/apis/v1/masters/courses` | List Courses |
| `GET` | `/apis/v1/masters/district` | Get All Districts |
| `GET` | `/apis/v1/masters/district/{id}` | Get Districts By State |
| `GET` | `/apis/v1/masters/languages` | List Government Health Programmes |
| `GET` | `/apis/v1/masters/languages/{id}` | Get Pi Languages By ID |
| `GET` | `/apis/v1/masters/medical-councils` | Get All Medical Council |
| `GET` | `/apis/v1/masters/medical-councils/name` | Get Medical Council By System Of Medicine Name |
| `GET` | `/apis/v1/masters/nurse-councils` | Get All Nurse Councils |
| `GET` | `/apis/v1/masters/states` | Get All States |
| `GET` | `/apis/v1/masters/states/{id}` | Get Status |
| `GET` | `/apis/v1/masters/sub-districts` | Get All Sub Districts |
| `GET` | `/apis/v1/masters/sub-districts/{id}` | Get All Sub Districts 1 |
| `GET` | `/apis/v1/masters/system-of-medicines` | Get All Medical System |
| `GET` | `/apis/v1/masters/universites` | List All University |
| `GET` | `/apis/v1/masters/universites/{id}` | Get University By College |
| `POST` | `/apis/v1/uploads/upload-document` | Upload Documents |
| `POST` | `/FacilityManagement/v1.5/facility/bygeoLocation/searchWithinRadiusWithFilter` | Get Facility Within Radius With Filter |
| `POST` | `/FacilityManagement/v1.5/facility/search` | Search Facility 1 |
| `GET` | `/getPsuDetailsByMinistry` | Get Psu Data |
| `GET` | `/hpid/get/categories` | Fetch HPID Categories |
| `GET` | `/hpid/get/subCategories` | Fetch HPID Sub Categories From Category |
| `POST` | `/password/change/byPassword` | Change Password |
| `POST` | `/password/recover/byAadhaar` | Recover Password Via Aadhaar |
| `POST` | `/password/recover/byMobile/sendMobileOTP` | Generate Mobile OTP 1 |
| `POST` | `/password/recover/byMobile/verifyMobileOTP` | Verify Mobile OTP 1 |
| `POST` | `/password/recover/confirmByAadhaar` | Recover Password Confirm By Aadhaar |
| `POST` | `/password/reset/password` | Reset Password And Session |
| `POST` | `/password/resetPassword` | Reset Password |
| `POST` | `/search/address/filter/deduplicate` | Get Filtered Address Post |
| `POST` | `/v1.0/facility/search-facilities` | Search Facility |
| `POST` | `/v1.5/facility/additional-information` | V15Facility Additional Information |
| `POST` | `/v1.5/facility/basic-information` | V15Basic Facility Information |
| `POST` | `/v1.5/facility/detailed-information` | V15Facility Detailed Information |
| `POST` | `/v1.5/facility/fetch-facility-Sub-type` | Get All Facility Sub Type By Facility Type |
| `POST` | `/v1.5/facility/fetch-facility-type` | Get All Facility Type By Ownership And Sys Of Med |
| `GET` | `/v1.5/facility/get-master-data` | Get Master Data |
| `GET` | `/v1.5/facility/get-master-types` | Get All Master Type |
| `POST` | `/v1.5/facility/get-owner-subtype` | Get All Sub Types By Owner Ship Type And Sub Type |
| `POST` | `/v1.5/facility/get-specialities` | Get All Specialities By System Of Medicine Code |
| `GET` | `/v1.5/facility/lgd/districts` | Get All District By State ID |
| `GET` | `/v1.5/facility/lgd/states` | Get All States By LGD |
| `GET` | `/v1.5/facility/lgd/subdistricts` | Get All Sub District By District Code |
| `POST` | `/v1.5/facility/sendOtpToContact` | Send OTP To Contact |
| `POST` | `/v1.5/facility/submit-facility` | V15Submit Facility Details |
| `POST` | `/v1.5/facility/validateOtp` | Validate OTP |
| `GET` | `/v1/account/getIdCard` | Get Account Png Card |
| `GET` | `/v1/account/information` | Get User Profile By JWT |
| `GET` | `/v1/account/user-details/{hprId}` | Get User Details |
| `POST` | `/v1/bridges/MutipleHRPAddUpdateServices` | Facility Add And Update |
| `POST` | `/v1/forgot/hprId/aadhaar` | Retrieval Health ID By Aadhaar |
| `POST` | `/v1/forgot/hprId/mobile` | Retrieval Health ID By Mobile |
| `POST` | `/v1/forgot/hprId/mobile/generateOtp` | Generate Mobile OTP |
| `POST` | `/v1/registration/aadhaar/checkHpIdAccountExist` | Account Exist |
| `POST` | `/v1/registration/aadhaar/generateMobileOTP` | Generate Mobile OTP |
| `POST` | `/v1/registration/aadhaar/hpid/suggestion` | Get Suggesstion |
| `POST` | `/v1/registration/aadhaar/verifyMobileOTP` | Verify Mobile OTP |
| `GET` | `/v1/search/existsByHprId/{hprId}` | Exists By HPR ID |
| `GET` | `/v1/search/searchByHprId/{hprId}` | Search User By HPR ID |
| `GET` | `/v1/search/searchByMobile/{mobile}` | Search User By Mobile No |
| `POST` | `/v2/registration/aadhaar/createHprIdWithPreVerified` | Create HPR ID V2 |
| `POST` | `/v2/registration/aadhaar/demographicAuthViaMobile` | Demographic Auth Via Mobile |
| `POST` | `/v2/registration/aadhaar/verifyOTP` | Verify OTP |
| `GET` | `/v4/auth/logout` | Logout |
## Headers
| Header | What it is |
| --- | --- |
| `Authorization` | |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `x-hprid-auth` | The HPR token of the signed-in professional, from the HPR login. |
| `x-hprid-auth-verifier` | The HPR token of the professional verifying the facility submission. |
| `X-Token` | The HPR token of the signed-in professional, from the HPR login. |
## A request, in full
```bash
curl --request POST \
--url https://apihspsbx.abdm.gov.in/v4/int/aadhaar/generateLink \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'Content-Type: application/json' \
--data '{
"scopes": [
"nhpr-register"
],
"source": "NHPR"
}'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
---
name: abdm-p1
description: Use when building, debugging or testing ABDM P1 in a PHR app: creating an ABHA address and logging in to it.
type: skill
domain: p1
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
produces:
- phr-login
- abha-profile
can_execute: true
can_orchestrate: false
---
# ABDM P1, PHR registration and login
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p1/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P1
- PHR certificate and session token
- Create ABHA number, Aadhaar OTP
- Create ABHA address, mobile number
- Create ABHA address, ABHA number with ABHA OTP
- Create ABHA address, ABHA number with Aadhaar OTP
- PHR login, mobile number
- PHR login, ABHA address with mobile OTP
- PHR login, ABHA number with Aadhaar OTP
- PHR login, ABHA number with ABHA OTP
- PHR login, ABHA address with password
- PHR login, Aadhaar OTP
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Register a new user in this PHR app and log them in"
- "What has to be in place before my first P1 call?"
- "My P1 call returned ABDM-1107. What is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 15 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 4 error codes from the specification's examples. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p1
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,48 @@
# HIE-CM p1 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### 900902
**Specification example:** HTTP 401, `Missing Credentials`, on `p1_get_v3_phr_app_enrollment_isexists`.
**Exit condition: the original call now succeeds.**
### ABDM-1107
**Specification example:** HTTP 400, `Invalid combinations of scopes`, on `p1_post_v3_phr_app_login_verify`.
**Exit condition: the original call now succeeds.**
### ABDM-1211
**Specification example:** HTTP 400, `User not found.`, on `p1_post_v3_phr_app_login_search`.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**Specification example:** HTTP 400, `Invalid LoginId`, on `p1_post_v3_phr_app_enrollment_request_otp`.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/p1
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `900902` | 401 | Missing Credentials | `p1_get_v3_phr_app_enrollment_isexists` |
| `ABDM-1107` | 400 | Invalid combinations of scopes | `p1_post_v3_phr_app_login_verify` |
| `ABDM-1211` | 400 | User not found. | `p1_post_v3_phr_app_login_search` |
| `ABDM-9999` | 400 | Invalid LoginId | `p1_post_v3_phr_app_enrollment_request_otp` |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,74 @@
# Integrate P1, PHR registration and login
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://abhasbx.abdm.gov.in` ABHA service, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
15 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/abha/api/v3/phr/app/enrollment/enrol` | 3 flows: Enrol ABHA Address |
| `GET` | `/abha/api/v3/phr/app/enrollment/isExists` | 3 flows: isExists API, isExists API Copy |
| `POST` | `/abha/api/v3/phr/app/enrollment/request/otp` | 3 flows: OTP Request - Mobile, OTP Request - ABHA OTP, OTP Request - AADHAR OTP |
| `POST` | `/abha/api/v3/phr/app/enrollment/suggestion` | 3 flows: Suggestion API |
| `POST` | `/abha/api/v3/phr/app/enrollment/verify` | 3 flows: OTP Verify - Mobile, OTP Verify - ABHA OTP, OTP Verify - AADHAR OTP |
| `GET` | `/abha/api/v3/phr/app/login/public/certificate` | PHR Certificate |
| `POST` | `/abha/api/v3/phr/app/login/request/otp` | 5 flows: OTP Request - Mobile, OTP Request - ABHAADDRES Mobile, OTP Request - A… |
| `POST` | `/abha/api/v3/phr/app/login/search` | Search Auth Methods - ABHAAddress |
| `POST` | `/abha/api/v3/phr/app/login/verify` | 6 flows: Login OTP Verify - Mobile, Login OTP Verify - ABHAADDRES Mobile, Login… |
| `POST` | `/abha/api/v3/phr/app/login/verify/user` | 5 flows: Verify User, Verify - User |
| `POST` | `/abha/api/v3/profile/account/request/emailVerificationLink` | Email Verification Link |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for each request. |
| `TIMESTAMP` | Request timestamp in UTC, ISO-8601 with Z. |
| `T-token` | The transaction token from the preceding login step, sent with a `Bearer ` prefix. It is valid only for that … |
| `X-token` | The user token from a login or enrolment response, sent with a `Bearer ` prefix. It acts for that ABHA holder. |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://abhasbx.abdm.gov.in/abha/api/v3/phr/app/enrollment/enrol \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'Content-Type: application/json' \
--data '{
"txnId": "27d444b7-2a3d-46d8-bf67-e5590b6c46b6",
"phrDetails": {
"mobile": "<ENCRYPTED_MOBILE>",
"firstName": "John",
"middleName": "",
"lastName": "Doe",
"yearOfBirth": "<DOB>",
"dayOfBirth": "",
"monthOfBirth": "<DOB>",
"gender": "M",
"email": "",
"profilePhoto": "",
"address": "<ADDRESS>",
"stateName": "Maharashtra",
"stateCode": "27",
"districtName": "<ADDRESS>",
"districtCode": "123",
"pinCode": "<PINCODE>",
"abhaAddress": "<ABHA_ADDRESS>",
"password": "<ENCRYPTED_PASSWORD>"
}
}'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
---
name: abdm-p2
description: Use when building, debugging or testing ABDM P2 in a PHR app: the PHR profile, linking an ABHA number, switching profiles, and linking, sharing and consent for the patient.
type: skill
domain: p2
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- phr-login
produces:
- consent-decision
can_execute: true
can_orchestrate: false
---
# ABDM P2, Consents Management
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p2/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P2
- PHR profile, update mobile
- PHR profile, update password
- Link ABHA number, ABHA OTP
- Link ABHA number, Aadhaar OTP
- PHR profile, switch profile
- PHR profile, card, QR code and session
- Quick OPD Registration
- User initiated linking
- Consent manager, HIU and HIP
- Consent manager, fetch and manage consent requests
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Link this user's records to their ABHA address from the PHR app"
- "What has to be in place before my first P2 call?"
- "My P2 call returned ABDM-9999. What is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 39 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 2 error codes from the specification's examples. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p2
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,34 @@
# HIE-CM p2 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### 900902
**Specification example:** HTTP 401, `Missing Credentials`, on `p2_get_v3_phr_app_login_profile`.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**Specification example:** HTTP 400, `Invalid LoginId`, on `p2_post_v3_phr_app_login_profile_request_otp`.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/p2
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `900902` | 401 | Missing Credentials | `p2_get_v3_phr_app_login_profile` |
| `ABDM-9999` | 400 | Invalid LoginId | `p2_post_v3_phr_app_login_profile_request_otp` |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,79 @@
# Integrate P2, Consents Management
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://abhasbx.abdm.gov.in` PHR application service, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
39 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/abha/api/v3/phr/app/login/profile` | Get Profile |
| `POST` | `/abha/api/v3/phr/app/login/profile/link` | 2 flows: Link Request |
| `GET` | `/abha/api/v3/phr/app/login/profile/phrCard` | Get PHR Card |
| `GET` | `/abha/api/v3/phr/app/login/profile/qrCode` | Get QR Code |
| `GET` | `/abha/api/v3/phr/app/login/profile/request/logout` | Logout |
| `POST` | `/abha/api/v3/phr/app/login/profile/request/otp` | 3 flows: Send OTP - Update Mobile, Send ABHA OTP - Link-DeLink, Send Aadhaar OT… |
| `GET` | `/abha/api/v3/phr/app/login/profile/request/token` | Refresh Token |
| `GET` | `/abha/api/v3/phr/app/login/profile/switch-profile` | Switch Profile |
| `POST` | `/abha/api/v3/phr/app/login/profile/updateProfile` | Update Profile |
| `POST` | `/abha/api/v3/phr/app/login/profile/verify` | 4 flows: Verify OTP - Update Mobile, Verify Password - Update Password, Verify … |
| `POST` | `/abha/api/v3/phr/app/login/profile/verify/switch-profile/user` | Verify User Switch Profile |
| `GET` | `/api/hiecm/consent/v3/artefact` | Fetch all the consent artefact details of a patient. |
| `GET` | `/api/hiecm/consent/v3/artefact/{consentId}` | Fetch the consent artefact details associated with the artefact-ID. |
| `GET` | `/api/hiecm/consent/v3/artefact/request/{consentRequestId}` | Fetch all the consent artefact details associated with a consent request REQUES… |
| `POST` | `/api/hiecm/consent/v3/auto/approve` | Setup an auto-approval policy for given HIU. |
| `POST` | `/api/hiecm/consent/v3/auto/approve/{consentId}/disable` | Disable the auto-approval policy. |
| `POST` | `/api/hiecm/consent/v3/auto/approve/{consentId}/enable` | Enable the auto-approval policy. |
| `GET` | `/api/hiecm/consent/v3/request` | Fetch all the consent request details of a patient. |
| `GET` | `/api/hiecm/consent/v3/request/{consentRequestId}` | Get the consent request details by REQUEST-ID. |
| `POST` | `/api/hiecm/consent/v3/request/{consentRequestId}/approve` | Approve the consent request raised by HIU from PHR/mobile application. |
| `POST` | `/api/hiecm/consent/v3/request/{consentRequestId}/deny` | Deny the consent request raised by HIU from PHR/mobile application. |
| `POST` | `/api/hiecm/consent/v3/revoke` | Revoke the granted consent from PHR/mobile application. |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `GET` | `/api/hiecm/gateway/v3/govt-programs` | Fetch the list of govt programmes. |
| `GET` | `/api/hiecm/gateway/v3/providers` | Fetch the list of providers filtered by name. |
| `GET` | `/api/hiecm/gateway/v3/providers/{hip-id}` | Fetch the record for provider details for requested provider ID. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `GET` | `/api/hiecm/hip/v3/link/patient/links` | This is the PHR APP API, this API will used to fetch all link care-context for … |
| `GET` | `/api/hiecm/patient-share/v3/profile/getTokenDetails` | OPD Token History |
| `POST` | `/api/hiecm/patient-share/v3/share` | OPD token generation |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/confirm` | Confirm his/her health records. |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/init` | Link his/her health records. |
| `POST` | `/api/hiecm/user-initiated-linking/v3/patient/care-context/discover` | Discover his/her health records. |
| `POST` | `/api/v3/hiu/patient/care-context/on-confirm` | Receive confirmation of the linked care contexts for a patient. It provides det… |
| `POST` | `/api/v3/hiu/patient/care-context/on-discover` | Receive the discovered care contexts of a patient. It provides detailed informa… |
| `POST` | `/api/v3/hiu/patient/care-context/on-init` | Receive the initial linking of care contexts for a patient. It provides detaile… |
| `POST` | `/api/v3/hiu/patient/on-share` | This API will be invoked to the HIU for sharing the response of HIECM's /api/hi… |
## Headers
| Header | What it is |
| --- | --- |
| `X-token` | The user token from a login or enrolment response, sent with a `Bearer ` prefix. It acts for that ABHA holder. |
| `REQUEST-ID` | Unique UUID for each request. |
| `TIMESTAMP` | Request timestamp in UTC, ISO-8601 with Z. |
| `X-AUTH-TOKEN` | The user token issued at login. Send it beside X-token. |
| `R-token` | The refresh token from a login response, sent with a `Bearer ` prefix, exchanged for a new user token. |
| `T-token` | The transaction token from the preceding login step, sent with a `Bearer ` prefix. It is valid only for that … |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://abhasbx.abdm.gov.in/abha/api/v3/phr/app/login/profile \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'X-token: Bearer <JWT TOKEN>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-AUTH-TOKEN: <X_AUTH_TOKEN>'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
---
name: abdm-p3
description: Use when building, debugging or testing ABDM P3 in a PHR app: reading, approving, denying, enabling, disabling and updating the patient's subscriptions and subscription requests, and the subscription request and notifications on the health locker side.
type: skill
domain: p3
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- phr-login
produces:
- subscription
can_execute: true
can_orchestrate: false
---
# ABDM P3, PHR subscriptions
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p3/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P3
- Subscription request and notifications, HIU side
- Subscription approval and management, PHR side
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Let this user approve or deny a subscription request"
- "What has to be in place before my first P3 call?"
- "My P3 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 18 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p3
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug P3, PHR subscriptions
The codes this module can return, with the message and the operation that returns each.
## Errors
The P3, PHR subscriptions specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,71 @@
# Design P3, PHR subscriptions
What the integration has to do to the journey around the calls: how many questions a patient is asked, where a failure is shown, and what a screen is forbidden to claim. Every rule below comes from a Catalogue atom, cited at the end.
## The five things a personal health record application must let a person do with consent
### In plain words
Consent is granted by a person, and the PHR app is where they do it. There is
a floor of five capabilities, and an app missing one leaves a person able to
give access they cannot inspect, change or withdraw.
1. **See the request**, with the HIU asking, the purpose, the record types,
the date range of records, how long the consent would last, and its status.
2. **Change it before allowing it**, where the request permits: the access
duration, the record date range, the categories shared, and the validity
period. This is the one most often left out, and the one that turns a
consent screen into a negotiation rather than a demand.
3. **Allow or refuse it.** The consent flow has three outcomes, not two:
approve, reject and ignore. An ignored request expires on the requester's
window, and the interface has to show that state.
4. **See what is already allowed**, so the person can tell which
organisations hold access right now. A list of past decisions is not the
same thing.
5. **Take it back** at any time. Two things follow: the status updates at the
consent manager, and sharing under that consent stops immediately, not at
the end of the period.
### What happens
Build a screen for each of the five. Approving posts to `/api/hiecm/consent/v3/request/{consentRequestId}/approve`, denying to `/api/hiecm/consent/v3/request/{consentRequestId}/deny`, and revoking a granted consent to `/api/hiecm/consent/v3/revoke`. Show Expired as its own state, never as Denied, because the person refused nothing.
### How you know it worked
One request goes through the whole arc in the app: seen, its date range narrowed, allowed, found in the list of live consents, and revoked. A fetch attempted under it afterwards fails.
### When it goes wrong
A screen that lists requests but not live consents leaves the person unable to revoke what they cannot see. Revocation shown as expiry hides a decision the person made. Leaving out modification turns consent into a notice.
## Subscriptions, and why a personal health record application needs one
### In plain words
Ask the user for consent before you create a subscription. An approved
subscription notifies your app when a care context is linked or updated. Surface these as device
notifications.
You need screens to list subscriptions, approve them, deny them and edit them.
Editing covers health information types, purpose, categories and the time period.
A subscription tells you a record exists. It is not consent, and it gives nobody
the record: reading it still needs a consent request, which is why a
subscription usually runs beside an auto approval policy.
### What happens
Raise the subscription with `/api/hiecm/subscription-requests/v3/init`; the request id arrives on `/api/v3/hiu/hiecm/subscription-requests/on-init`. The person approves it with `/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/approve` or denies it. Once approved, notifications arrive on `/api/v3/hiu/subscription/notify`, in the categories `LINK` and `DATA`, and you acknowledge each with `/api/hiecm/subscription-requests/v3/hiu/care-context/on-notify`. Set one up when you create an address and when a person signs in with an address the install has not seen.
### How you know it worked
The subscription shows as approved, and linking a care context to that address from a facility produces a notification on your callback without any call from you.
### When it goes wrong
Nothing arrives: the checks are those for any callback, see [the callback never arrives](/docs/hiecm/v3/troubleshooting/callback-never-arrives). A subscription created without asking the person is a consent failure that certification looks for. A notification acted on as though it were permission reads a record with no consent behind it.
## Where these came from
- `hiecm.concept.consent-in-a-phr-app`
- `hiecm.concept.phr-subscriptions`

View File

@ -0,0 +1,53 @@
# Integrate P3, PHR subscriptions
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
18 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `GET` | `/api/hiecm/subscription-requests/v3/{subscriptionID}` | Fetch his/her subscription details by subscription ID. |
| `POST` | `/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/approve` | Approve subscription request. |
| `POST` | `/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/deny` | Deny subscription request. |
| `POST` | `/api/hiecm/subscription-requests/v3/disable/{subscriptionID}` | Disable the subscription by subscription ID. |
| `POST` | `/api/hiecm/subscription-requests/v3/enable/{subscriptionID}` | Enable the subscription by subscription ID. |
| `POST` | `/api/hiecm/subscription-requests/v3/hiu/care-context/on-notify` | Respond to /api/v3/hiu/subscription/notify. |
| `POST` | `/api/hiecm/subscription-requests/v3/hiu/on-notify` | Respond to /subscription-requests/hiu/notify. |
| `POST` | `/api/hiecm/subscription-requests/v3/init` | Initiate subscription request. |
| `PUT` | `/api/hiecm/subscription-requests/v3/patients/{subscriptionID}` | Edit the subscription details. |
| `GET` | `/api/hiecm/subscription-requests/v3/request/{subscriptionRequestId}` | Fetch his/her subscription details by subscription REQUEST-ID. |
| `GET` | `/api/hiecm/subscription-requests/v3/requests` | Fetch his/her subscription requests details. |
| `POST` | `/api/v3/hiu/hiecm/subscription-requests/on-init` | This is a callback API for /api/hiecm/subscription-requests/v3/init. |
| `POST` | `/api/v3/hiu/subscription-requests/hiu/notify` | This is a callback API when a subscription request is approved or denied. |
| `POST` | `/api/v3/hiu/subscription/notify` | This is a callback API to notify the subscribed HIU when a care context is link… |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-AUTH-TOKEN` | JWT Authentication token which was issued by ABDM after successful validation of username and password |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,484 @@
# HIE-CM p3 build
Scaffolds an ABDM p3 integration one journey at a time. It covers reading, approving, denying, enabling, disabling and updating the patient's subscriptions and subscription requests, and the subscription request and notifications on the health locker side.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Subscription request and notifications, HIU side (`p3-subscription-hiu`)
**Act: the calls in this journey, in order**
#### 1. Initiate subscription request (`p3_post_subscription_requests_v3_init`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/init \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"subscription": {
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"patient": {
"id": "<ABHA_ADDRESS>"
},
"hiu": {
"id": "INDIA_HIU",
"name": "INDIA HIU",
"type": "HIU"
},
"hips": [
{
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
}
],
"categories": [
"LINK"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
}
}
}'
```
#### 2. Receive the HIU subscription requests on init (`p3_post_v3_hiu_hiecm_subscription_requests_on_init`)
Inbound to your bridge at `/api/v3/hiu/hiecm/subscription-requests/on-init`. Acknowledge it and continue.
#### 3. Notify subscription requests HIU (`p3_post_v3_hiu_subscription_requests_hiu_notify`)
Inbound to your bridge at `/api/v3/hiu/subscription-requests/hiu/notify`. Acknowledge it and continue.
#### 4. Answer the subscription request notification (`p3_post_subscription_requests_v3_hiu_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/hiu/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"subscriptionRequestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
},
"error": {
"code": "ABDM-1001",
"message": "No data found"
},
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
#### 5. Notify HIU subscription (`p3_post_v3_hiu_subscription_notify`)
Inbound to your bridge at `/api/v3/hiu/subscription/notify`. Acknowledge it and continue.
#### 6. Answer the care context subscription notification (`p3_post_subscription_requests_v3_hiu_care_context_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/hiu/care-context/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"eventId": "3c2f0e59-8388-4698-9fe6-05db67aeac46"
},
"error": {
"code": "ABDM-1001",
"message": "No data found"
},
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/subscription-requests/v3/init` is answered by `POST /api/v3/hiu/hiecm/subscription-requests/on-init` on your bridge.
### Subscription approval and management, PHR side (`p3-subscription-phr`)
**Act: the calls in this journey, in order**
#### 1. Approve subscription request (`p3_post_subscription_requests_v3_request_id_approve`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/approve \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"isApplicableForAllHIPs": true,
"includedSources": [
{
"hiTypes": [
"Prescription",
"DiagnosticReport",
"OPConsultation",
"DischargeSummary",
"ImmunizationRecord",
"HealthDocumentRecord",
"WellnessRecord",
"Invoice"
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "www.abdm.gov.in"
},
"categories": [
"LINK",
"DATA"
],
"period": {
"from": "2025-01-09T09:00:00.000Z",
"to": "2124-12-31T09:00:00.000Z"
}
}
],
"excludedSources": []
}'
```
#### 2. Deny subscription request (`p3_post_subscription_requests_v3_request_id_deny`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/deny \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"reason": "Subscription denied."
}'
```
#### 3. Fetch his/her subscription requests details (`p3_get_subscription_requests_v3_requests`)
```bash
curl --request GET \
--url "https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/requests?limit=5&offset=5&status=ALL" \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 4. Edit the subscription details (`p3_put_subscription_requests_v3_patients_subscription_id`)
```bash
curl --request PUT \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/patients/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"hiuId": "INDIA_HIU",
"subscriptionEditAndApprovalRequest": {
"isApplicableForAllHIPs": true,
"includedSources": [
{
"hiTypes": [
"Prescription"
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"hip": {
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
},
"categories": [
"LINK"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
},
"status": "SUCCESS"
}
],
"excludedSources": [
{
"hiTypes": [
"Prescription"
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"hip": {
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
},
"categories": [
"LINK"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
},
"status": "SUCCESS"
}
]
}
}'
```
#### 5. Disable the subscription by subscription ID (`p3_post_subscription_requests_v3_disable_subscription_id`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/disable/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 6. Enable the subscription by subscription ID (`p3_post_subscription_requests_v3_enable_subscription_id`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/enable/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 7. Fetch his/her subscription details by subscription REQUEST-ID (`p3_get_subscription_requests_v3_request_request_id`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/request/{subscriptionRequestId} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 8. Fetch his/her subscription details by subscription ID (`p3_get_subscription_requests_v3_subscription_id`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
**Exit condition (Observe until this is true)**
A 200 whose body matches:
```json
{
"subscriptionId": "f29f0e59-8388-4698-9fe6-05db67aeac46",
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"dateCreated": "2021-09-28T12:30:08.573Z",
"status": "GRANTED",
"dateGranted": "2021-09-28T12:30:08.573Z",
"patient": {
"id": "<ABHA_ADDRESS>"
},
"requester": {
"id": "<ABHA_ADDRESS>",
"name": "ABDM_HIU",
"type": "HIU"
},
"includedSources": [
{
"hip": {
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
},
"categories": [
"LINK"
],
"hiTypes": [
"Prescription"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
},
"status": "GRANTED"
}
]
}
```
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/p3
- Error codes: /docs/hiecm/v3/api/p3/errors

View File

@ -0,0 +1,77 @@
---
name: abdm-p4
description: Use when building, debugging or testing ABDM P4, health lockers: setting up a locker and listing the lockers and requests on an ABHA address.
type: skill
domain: p4
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- phr-login
produces:
- locker-record
can_execute: true
can_orchestrate: false
---
# ABDM P4, health lockers
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p4/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P4
- Locker
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Set up a health locker for this user"
- "What has to be in place before my first P4 call?"
- "My P4 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 9 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p4
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug P4, health lockers
The codes this module can return, with the message and the operation that returns each.
## Errors
The P4, health lockers specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,44 @@
# Integrate P4, health lockers
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
9 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `GET` | `/api/hiecm/gateway/v3/health-lockers` | Fetch the record with health locker enabled provider details. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `GET` | `/api/hiecm/subscription-requests/v3/patients/lockers` | The API provides the list of health locker that the ABHA address is subscribed … |
| `GET` | `/api/hiecm/subscription-requests/v3/patients/lockers/{locker-id}` | Get health locker settings of a patient by locker ID. |
| `GET` | `/api/hiecm/subscription-requests/v3/patients/requests` | Get all the consent and subscription requests with given filters. |
| `POST` | `/api/hiecm/subscription-requests/v3/setup-locker` | Setup health locker for a patient. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-AUTH-TOKEN` | JWT Authentication token issued by ABDM after successful validation of username and password |
| `X-LOCKER-ID` | The locker id |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,222 @@
# HIE-CM p4 build
Scaffolds an ABDM p4 integration one journey at a time. It covers setting up a health locker and listing the lockers and requests on an ABHA address.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Locker (`p4-locker`)
**Act: the calls in this journey, in order**
#### 1. Setup health locker for a patient (`p4_post_subscription_requests_v3_setup_locker`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/setup-locker \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'X-LOCKER-ID: <X_LOCKER_ID>'
```
#### 2. Get the subscription requests patients lockers (`p4_get_subscription_requests_v3_patients_lockers`)
```bash
curl --request GET \
--url "https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/patients/lockers?includeInactive=true" \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 3. Get health locker settings of a patient by locker ID (`p4_get_subscription_requests_v3_patients_lockers_lockerid`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/patients/lockers/{locker-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 4. Get all the consent and subscription requests with given filters (`p4_get_subscription_requests_v3_patients_requests`)
```bash
curl --request GET \
--url "https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/patients/requests?consentLimit=<CONSENTLIMIT>&consentOffset=<CONSENTOFFSET>&subscriptionLimit=<SUBSCRIPTIONLIMIT>&subscriptionOffset=<SUBSCRIPTIONOFFSET>&status=ALL" \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 5. Fetch the record with health locker enabled provider details (`p4_get_gateway_v3_health_lockers`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/health-lockers \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
**Exit condition (Observe until this is true)**
A 200 whose body matches:
```json
[
{
"identifier": {
"name": "AB - PMJAY",
"id": "PMJAY"
},
"facilityType": [
"HIP"
],
"isHip": true,
"isGovtEntity": false,
"endpoints": {
"healthLockerEndpoints": [
{
"use": "registration",
"connectionType": "HTTPS",
"address": "https://abc.com/register"
}
]
}
}
]
```
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/p4

View File

@ -0,0 +1,80 @@
---
name: abdm-record-share
description: Use when building, debugging or testing ABDM patient scan and record share: a PHR app shares chosen records with an HIU after scanning its QR code, on either side.
type: skill
domain: record-share
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- hip-registration
- callback-url
produces:
- shared-record
can_execute: true
can_orchestrate: false
---
# ABDM Patient scan and record share
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-record-share/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with Patient scan and record share
- Record share, PHR app side
- Record share, HIU side
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Receive the records a patient shares from a PHR app after scanning our QR code"
- "What has to be in place before my first Patient scan and record share call?"
- "My Patient scan and record share call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 12 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/record-share
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug Patient scan and record share
The codes this module can return, with the message and the operation that returns each.
## Errors
The Patient scan and record share specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,50 @@
# Integrate Patient scan and record share
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox. X-CM-ID is sbx.
- `https://apis.abdm.gov.in` ABDM gateway, production. X-CM-ID is abdm.
## Endpoints
12 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `GET` | `/api/hiecm/patient-record/v3/audit-history` | List the record sharing history of the signed in user. |
| `POST` | `/api/hiecm/patient-record/v3/notify` | Notify the HIE-CM of the transfer status, once, after the data transfer complet… |
| `POST` | `/api/hiecm/patient-record/v3/on-share` | Reply to a share request with the data push URL and the encryption key. |
| `POST` | `/api/hiecm/patient-record/v3/share` | Share a patient's records with the HIU whose QR code was scanned. |
| `POST` | `/api/v3/patient-record/on-notify` | Receive the other side's transfer status. |
| `POST` | `/api/v3/patient-record/on-share` | Receive the HIU's data push URL and encryption key, as the PHR app. |
| `POST` | `/api/v3/patient-record/share` | Receive a patient's share request, as the HIU. |
| `POST` | `/health-information/transfer` | Receive the encrypted records at the data push URL, as the HIU. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-AUTH-TOKEN` | JWT access token issued by the PHR service after successful user authentication. If the HIP does not have any… |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended. |
| `request-id` | Random UUID, a v4 style guid, unique per callback. |
| `timestamp` | ISO 8601 timestamp of when the callback was sent. |
| `x-hiu-id` | Identifier of the health information user to which the request was intended. |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,379 @@
# HIE-CM record-share build
Scaffolds an ABDM record-share integration one journey at a time. It covers a patient sharing chosen records with an HIU from a PHR app after scanning its QR code.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Record share, PHR app side (`record-share-phr`)
**Act: the calls in this journey, in order**
#### 1. Share a patient's records with the HIU whose QR code was scanned (`record-share_post_patient_record_v3_share`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/patient-record/v3/share \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-HIU-ID: HIU_ID' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: Bearer <USER_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"metaData": {
"hiuId": "MANISH_HIU",
"counterId": "IN081010313"
},
"profile": {
"patient": {
"abhaNumber": null,
"abhaAddress": "abhaAddress@sbx",
"name": "String",
"gender": "M",
"dayOfBirth": "18",
"monthOfBirth": "10",
"yearOfBirth": "1991",
"address": {
"line": "String",
"district": "String",
"state": "String",
"pincode": "XXXXXX"
},
"phoneNumber": "989141XXXX"
}
},
"sharedRecordCount": 2,
"consent": {
"careContexts": [
{
"patientReference": "manishk1991@sbx",
"careContextReference": "10004-20200001768-1"
},
{
"patientReference": "manishk1991@sbx",
"careContextReference": "10004-20200001768-2"
}
],
"permission": {
"accessMode": "VIEW"
},
"dataEraseAt": "2025-10-11T08:58:09.738Z"
}
}'
```
#### 2. Receive the HIU's data push URL and encryption key, as the PHR app (`record-share_post_v3_patient_record_on_share`)
Inbound to your bridge at `/api/v3/patient-record/on-share`. Acknowledge it and continue.
#### 3. Push the encrypted records to the HIU's data push URL, as the PHR app (`record-share_post_health_information_transfer`)
Outbound from your app. POST the encrypted records to the `dataPushUrl` the HIU sent in the on-share of step 2. The call goes straight to the HIU, not through the gateway. The HIU answers 202.
#### 4. Notify patient record (`record-share_post_patient_record_v3_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/patient-record/v3/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-HIU-ID: HIU_ID' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"transactionId": "a0e7bed4-ef98-4ea8-9077-f4a6192bfe2b",
"doneAt": "2026-03-19T08:01:11.090Z",
"statusNotification": {
"sessionStatus": "TRANSFERRED",
"statusResponses": [
{
"careContextReference": "COC497647c1-0627-48fa-8131-0dddc1b3e0b4",
"hiStatus": "DELIVERED",
"description": "Data sent successfully"
},
{
"careContextReference": "COC497647c1-0627-48fa-8131-0dddc1b3e0b5",
"hiStatus": "DELIVERED",
"description": "Data sent successfully"
}
]
}
}
}'
```
#### 5. Receive the other side's transfer status (`record-share_post_v3_patient_record_on_notify`)
Inbound to your bridge at `/api/v3/patient-record/on-notify`. Acknowledge it and continue.
#### 6. List the record sharing history of the signed in user (`record-share_get_patient_record_v3_audit_history`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/patient-record/v3/audit-history \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: Bearer <USER_TOKEN>'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/patient-record/v3/share` is answered by `POST /api/v3/patient-record/share` on your bridge.
- `POST /api/hiecm/patient-record/v3/notify` is answered by `POST /api/v3/patient-record/on-notify` on your bridge.
Then `GET /api/hiecm/patient-record/v3/audit-history` returns a 200 whose body matches:
```json
[
{
"requestId": "ed9f451d-9e07-484d-ab68-9972d6529d68",
"abhaAddress": "manishk1991@sbx",
"senderFacilityId": "MANISH_PHR_TEST",
"senderFacilityName": "MANISH-PHR-TEST",
"receiverFacilityId": "MANISH_HIU",
"receiverFacilityName": "MANISH_HIU",
"counterCode": "f4cd0d8e-25ec-465f-993c-01c91a56e0e4",
"transactionId": "f413b202-16b6-4d4a-9900-5cdcfb8baa50",
"status": "RECORD_SHARE_REQUESTED",
"sharedRecordCount": 2,
"consent": {
"accessMode": "VIEW",
"dataEraseAt": "2026-10-12T08:58:09.738Z",
"careContexts": [
{
"patientReference": "manikandanb87@sbx",
"careContextReference": "COC497647c1-0627-48fa-8131-0dddc1b3e0b4"
},
{
"patientReference": "manikandanb87@sbx",
"careContextReference": "COC497647c1-0627-48fa-8131-0dddc1b3e0b5"
}
]
},
"dateCreated": "2025-12-08T12:04:53.594Z",
"dateModified": "2025-12-08T12:04:53.594Z"
}
]
```
### Record share, HIU side (`record-share-hiu`)
**Act: the calls in this journey, in order**
#### 1. Receive a patient's share request, as the HIU (`record-share_post_v3_patient_record_share`)
Inbound to your bridge at `/api/v3/patient-record/share`. Acknowledge it and continue.
#### 2. Submit the reply to a share request with the data push URL and the encryption key (`record-share_post_patient_record_v3_on_share`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/patient-record/v3/on-share \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-HIU-ID: HIU_ID' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"hiRequest": {
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"dataPushUrl": "https://webhook.site/a2477c41-2185-47eb-835a/health-information/transfer",
"keyMaterial": {
"cryptoAlg": "ECDH.",
"curve": "curve25519",
"dhPublicKey": {
"expiry": "2022-12-28T13:18:20.742Z",
"parameters": "Ephemeral public key.",
"keyValue": "BFN7KTdOT0jIAExG2A8Jg+01wMPWxptiGqwHRVvtiVEsUq2FR7P2UdqZxJyPJSeR6muai21iQhasNxnhh8I5M+g="
},
"nonce": "28236d89-cb13-479d-ad71-7a57d5f669a9"
}
},
"response": {
"requestId": "efd6964f-6893-4b66-9e4e-28afc164126b"
}
}'
```
#### 3. Receive the encrypted records at the data push URL, as the HIU (`record-share_post_health_information_transfer`)
Inbound to your bridge at `/health-information/transfer`. Acknowledge it and continue.
#### 4. Receive the other side's transfer status (`record-share_post_v3_patient_record_on_notify`)
Inbound to your bridge at `/api/v3/patient-record/on-notify`. Acknowledge it and continue.
#### 5. Notify patient record (`record-share_post_patient_record_v3_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/patient-record/v3/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-HIU-ID: HIU_ID' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"transactionId": "3acbbb52-6de0-441f-b452-3c9488462b25",
"doneAt": "2023-01-24T06:35:44.167Z",
"statusNotification": {
"sessionStatus": "RECEIVED",
"statusResponses": [
{
"careContextReference": "10004-20200001768-1",
"hiStatus": "VALID",
"description": "Data received successfully"
},
{
"careContextReference": "10004-20200001768-2",
"hiStatus": "ERRORED",
"description": "Data could not be decrypted"
}
]
}
}
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/patient-record/v3/on-share` is answered by `POST /api/v3/patient-record/on-share` on your bridge.
- `POST /api/hiecm/patient-record/v3/on-share` is answered by `POST /health-information/transfer` on your bridge.
- `POST /api/hiecm/patient-record/v3/notify` is answered by `POST /api/v3/patient-record/on-notify` on your bridge.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/record-share
- Error codes: /docs/hiecm/v3/api/record-share/errors

View File

@ -0,0 +1,82 @@
---
name: abdm-scan-and-pay
description: Use when building, debugging or testing ABDM scan and pay: open orders, patient selection and payment status between a facility and a PHR app.
type: skill
domain: scan-and-pay
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- facility-id
- callback-url
produces:
- order
- payment-status
can_execute: true
can_orchestrate: false
---
# ABDM Scan and pay
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-scan-and-pay/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with Scan and pay
- Scan and pay, HIP side
- Scan and pay, PHR side
- Scan and pay details and version update
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Open an order at this counter and take payment from a PHR app"
- "What has to be in place before my first Scan and pay call?"
- "My Scan and pay call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 22 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/scan-and-pay
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug Scan and pay
The codes this module can return, with the message and the operation that returns each.
## Errors
The Scan and pay specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,58 @@
# Integrate Scan and pay
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
22 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `PATCH` | `/api/hiecm/gateway/v3/scanPay/updateVersion` | Update version to the serviceId. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/on-selection` | Share payment bundle alone with procedures of the patient. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/on-share/open-order` | HIE-CM to send all the open order for patient. |
| `GET` | `/api/hiecm/scan-gateway/v3/patient/scan-pay/details` | This is retrieve the all the details of the user. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/scan-pay/notify` | Send the payment status to HIU. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/scan-pay/on-notify` | Notify to HIP so that confirm that the HIU received the payment status. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/scan-pay/on-order-status` | Check the status of reports. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/scan-pay/order-status` | Check the status of reports. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/selection` | Select the all open-order and send to HIP for a payment request detail. |
| `POST` | `/api/hiecm/scan-gateway/v3/patient/share/open-order` | Be invoked from the integrator application (any PHR application, just like ABHA… |
| `POST` | `/v3/patient/on-selection` | This is callback API for the API. This API needs to implement by HIU to receive… |
| `POST` | `/v3/patient/on-share/open-order` | This is a callback API for patient on-share. This API needs to implement by HIU… |
| `POST` | `/v3/patient/scan-pay/notify` | This is callback API for the notify API. This API needs to implement by HIU to … |
| `POST` | `/v3/patient/scan-pay/on-notify` | This is an callback API for on-notify API need to implement by HIP to received … |
| `POST` | `/v3/patient/scan-pay/on-order-status` | This is callback for the on-order-status API. This API needs to implement by HI… |
| `POST` | `/v3/patient/scan-pay/order-status` | This is callback API for the order_status API. This API needs to implement by H… |
| `POST` | `/v3/patient/selection` | This is the call back API for the selection API. This API needs to implement by… |
| `POST` | `/v3/patient/share/open-order` | Check the status of reports. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-AUTH-TOKEN` | JWT Authentication token which was issued by ABDM after successful validation of username and password |
| `X-HIP-ID` | Identifier of the health information provider to which the request was intended |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,543 @@
# HIE-CM scan-and-pay build
Scaffolds an ABDM scan-and-pay integration one journey at a time. It covers open orders, patient selection and payment status between a facility and a PHR app.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Scan and pay, HIP side (`scan-and-pay-abdm-scan-pay-hip`)
**Act: the calls in this journey, in order**
#### 1. Check the status of reports (`scan-and-pay_post_v3_patient_share_open_order`)
Inbound to your bridge at `/v3/patient/share/open-order`. Acknowledge it and continue.
#### 2. Submit the HIE-CM to send all the open order for patient (`scan-and-pay_post_scan_gateway_v3_patient_on_share_open_order`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/on-share/open-order \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"intent": "OPEN_PAYMENT_ORDER",
"abhaAddress": "<ABHA_ADDRESS>",
"patientUid": "string",
"procedures": [
{
"category": "OPD consultation",
"services": [
{
"name": "consultation",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Laboratory and Diagnostics",
"services": [
{
"name": "Diagnostics",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Pharmacy",
"services": [
{
"name": "Pharmacy",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Miscellaneous/Other",
"services": [
{
"name": "Miscellaneous",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
}
],
"response": {
"requestId": "6c3d4e5c-09d1-****-817b-a0c82d130c53"
}
}'
```
#### 3. Receive the patient selection (`scan-and-pay_post_v3_patient_selection`)
Inbound to your bridge at `/v3/patient/selection`. Acknowledge it and continue.
#### 4. Share payment bundle alone with procedures of the patient (`scan-and-pay_post_scan_gateway_v3_patient_on_selection`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/on-selection \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"intent": "PAYMENT_ORDER",
"openOrderRequestId": "b767614f-153a-4aa3-946f-1622596f0fab",
"abhaAddress": "<ABHA_ADDRESS>",
"procedures": [
{
"category": "OPD consultation",
"services": [
{
"name": "consultation",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Laboratory and Diagnostics",
"services": [
{
"name": "Diagnostics",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Pharmacy",
"services": [
{
"name": "Pharmacy",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
}
],
"paymentBundle": {
"paymentMode": "GATEWAY",
"paymentUrl": "string",
"orderNumber": "string",
"amount": 1250.55,
"merchantId": "123465",
"description": "Testing"
},
"response": {
"requestId": "6c3d4e5c-09d1-***-817b-a0c82d130c53"
}
}'
```
#### 5. Send the payment status to HIU (`scan-and-pay_post_scan_gateway_v3_patient_scan_pay_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/scan-pay/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "SUCCESS/ CANCELED/ PENDING/ FAIL/ REFUND_INITIATED/ REFUND_SUCCESS",
"abhaAddress": "<username>@sbx",
"transactionId": "string",
"orderNumber": "string",
"openOrderRequestId": "b767614f-153a-4aa3-946f-1622596f0fab",
"paymentDate": "2025-01-20T07:47:49.102Z",
"paymentRecipetLink": "PDF URL LINK of RECIPT"
}
}'
```
#### 6. Receive the patient scan pay on notify (`scan-and-pay_post_v3_patient_scan_pay_on_notify`)
Inbound to your bridge at `/v3/patient/scan-pay/on-notify`. Acknowledge it and continue.
#### 7. Receive the patient scan pay order status (`scan-and-pay_post_v3_patient_scan_pay_order_status`)
Inbound to your bridge at `/v3/patient/scan-pay/order-status`. Acknowledge it and continue.
#### 8. Check the status of reports (`scan-and-pay_post_scan_gateway_v3_patient_scan_pay_on_ord_21f376`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/scan-pay/on-order-status \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "SUCCESS/ CANCELED/ PENDING/ FAIL/ REFUND_INITIATED/ REFUND_SUCCESS",
"abhaAddress": "<username>@sbx",
"transactionId": "string",
"orderNumber": "string",
"openOrderRequestId": "Queried Request id",
"paymentDate": "Transaction Date UTC",
"paymentRecipetLink": "PDF URL LINK of RECIPT"
},
"response": {
"requestId": "b767614f-153a-***-946f-1622596f0fab"
}
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/scan-gateway/v3/patient/scan-pay/notify` is answered by `POST /v3/patient/scan-pay/on-notify` on your bridge.
### Scan and pay, PHR side (`scan-and-pay-abdm-hiecm-scan-pay-phr`)
**Act: the calls in this journey, in order**
#### 1. Share patient open order (`scan-and-pay_post_scan_gateway_v3_patient_share_open_order`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/share/open-order \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"intent": "OPEN_PAYMENT_ORDER",
"metaData": {
"hipId": "HIP_1",
"counterId": "123-456"
},
"profile": {
"patient": {
"abhaNumber": "91-7507-xxxx-xxxx",
"abhaAddress": "<ABHA_ADDRESS>",
"name": "name",
"gender": "M",
"dayOfBirth": "string",
"monthOfBirth": "string",
"yearOfBirth": "string",
"address": {
"line": "Address line 1",
"district": "XXXXXXX",
"state": "XXXXXX",
"pincode": "XXXXXX"
},
"phoneNumber": "987654xxxx"
}
}
}'
```
#### 2. Receive the patient on-share (`scan-and-pay_post_v3_patient_on_share_open_order`)
Inbound to your bridge at `/v3/patient/on-share/open-order`. Acknowledge it and continue.
#### 3. Select the all open-order and send to HIP for a payment request detail (`scan-and-pay_post_scan_gateway_v3_patient_selection`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/selection \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"intent": "PAYMENT_ORDER",
"openOrderRequestId": "b767614f-153a-***-946f-1622596f0fab",
"abhaAddress": "<ABHA_ADDRESS>",
"procedures": [
{
"category": "OPD consultation",
"services": [
{
"name": "consultation",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Laboratory and Diagnostics",
"services": [
{
"name": "Diagnostics",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
},
{
"category": "Pharmacy",
"services": [
{
"name": "Pharmacy",
"serviceId": "service-12345",
"description": "Albumin 24 hrs Urine",
"amount": 629.12
}
]
}
]
}'
```
#### 4. Receive the patient on selection (`scan-and-pay_post_v3_patient_on_selection`)
Inbound to your bridge at `/v3/patient/on-selection`. Acknowledge it and continue.
#### 5. Notify patient scan pay (`scan-and-pay_post_v3_patient_scan_pay_notify`)
Inbound to your bridge at `/v3/patient/scan-pay/notify`. Acknowledge it and continue.
#### 6. Notify to HIP so that confirm that the HIU received the payment status (`scan-and-pay_post_scan_gateway_v3_patient_scan_pay_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/scan-pay/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "SUCCESS/ CANCELED/ PENDING/ FAIL/ REFUND_INITIATED/ REFUND_SUCCESS",
"abhaAddress": "<username>@sbx",
"transactionId": "string",
"orderNumber": "string",
"openOrderRequestId": "b767614f-153a-4aa3-946f-1622596f0fab",
"paymentDate": "2025-01-20T07:47:49.102Z",
"paymentRecipetLink": "PDF URL LINK of RECIPT"
},
"response": {
"requestId": "b767614f-153a-***-946f-1622596f0fab"
}
}'
```
#### 7. Check the status of reports (`scan-and-pay_post_scan_gateway_v3_patient_scan_pay_order_status`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/scan-pay/order-status \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"queryStatus": {
"orderNumber": "string",
"abhaAddress": "<ABHA_ADDRESS>",
"openOrderRequestId": "0d8bd16b-117c-4d07-9916-109fe3a9ab88"
}
}'
```
#### 8. Receive the patient scan pay on order status (`scan-and-pay_post_v3_patient_scan_pay_on_order_status`)
Inbound to your bridge at `/v3/patient/scan-pay/on-order-status`. Acknowledge it and continue.
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/scan-gateway/v3/patient/share/open-order` is answered by `POST /v3/patient/on-share/open-order` on your bridge.
- `POST /api/hiecm/scan-gateway/v3/patient/selection` is answered by `POST /v3/patient/on-selection` on your bridge.
- `POST /api/hiecm/scan-gateway/v3/patient/scan-pay/order-status` is answered by `POST /v3/patient/scan-pay/on-order-status` on your bridge.
### Scan and pay details and version update (`scan-and-pay-utility`)
**Act: the calls in this journey, in order**
#### 1. Get the patient scan pay details (`scan-and-pay_get_scan_gateway_v3_patient_scan_pay_details`)
```bash
curl --request GET \
--url "https://dev.abdm.gov.in/api/hiecm/scan-gateway/v3/patient/scan-pay/details?status=SUCCESS&limit=10&startDate=2024-07-13T07:30:10.186Z&endDate=2025-02-18T08:30:50.189Z&offset=0" \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 2. Update version to the serviceId (`scan-and-pay_patch_gateway_v3_scanpay_updateversion`)
```bash
curl --request PATCH \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/scanPay/updateVersion \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"recordShareEnabled": true,
"scanPayEnabled": true,
"scanPayVersion": "v2",
"serviceId": [
"****_HIP, ***_HIU"
]
}'
```
**Exit condition (Observe until this is true)**
A 200 response. The specification gives no body for it, so read what comes back.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/scan-and-pay
- Error codes: /docs/hiecm/v3/api/scan-and-pay/errors

View File

@ -0,0 +1,79 @@
---
name: abdm-scan-and-register
description: Use when building, debugging or testing ABDM scan and register: receiving the profile a patient shares by QR code at a counter and answering with a queue token.
type: skill
domain: scan-and-register
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- facility-id
produces:
- abha-profile
- registration
can_execute: true
can_orchestrate: false
---
# ABDM Scan and register
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-scan-and-register/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with Scan and register
- Scan and register
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Register the patient who just scanned the counter QR code and hand them a token"
- "What has to be in place before my first Scan and register call?"
- "My Scan and register call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 6 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/use-cases/scan-and-register
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug Scan and register
The codes this module can return, with the message and the operation that returns each.
## Errors
The Scan and register specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,40 @@
# Integrate Scan and register
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
6 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/hiecm/patient-share/v3/on-share` | Sharing the response of HIECM's /api/hiecm/patient-share/v3/on-share API |
| `POST` | `/api/v3/hip/patient/share` | This API will be invoked to the HIP for sharing the response of HIECM's /api/hi… |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIP-ID` | Identifier of the health information provider to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,172 @@
# HIE-CM scan-and-register build
Scaffolds an ABDM scan-and-register integration one journey at a time. It covers receiving the profile a patient shares by QR code at a counter and answering with a queue token.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Scan and register (`scan-and-register-abdm-patient-share-hip`)
**Act: the calls in this journey, in order**
#### 1. Share HIP patient (`scan-and-register_post_v3_hip_patient_share`)
Inbound to your bridge at `/api/v3/hip/patient/share`. Acknowledge it and continue.
#### 2. Answer the patient share request (`scan-and-register_post_patient_share_v3_on_share`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/patient-share/v3/on-share \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "SUCCESS",
"abhaAddress": "<ABHA_ADDRESS>",
"profile": {
"context": "43",
"tokenNumber": "3",
"expiry": 180
}
},
"error": {
"code": "ABDM-9999",
"message": "Unknown exception"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
**Exit condition (Observe until this is true)**
A 202 on this reply, sent with `response.requestId` set to the `REQUEST-ID` of the `POST /api/v3/hip/patient/share` request it answers. A reply without that id answers nothing.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/scan-and-register
- Error codes: /docs/hiecm/v3/api/scan-and-register/errors

13
.cursor/mcp.json Normal file
View File

@ -0,0 +1,13 @@
{
"mcpServers": {
"abdm-docs": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://docs.abdm.gov.in/mcp"
],
"url": "https://docs.abdm.gov.in/mcp"
}
}
}

View File

@ -0,0 +1,66 @@
---
name: abdm-fhir
description: Use when producing or checking FHIR for ABDM: building NRCES compliant document bundle generation into a codebase, or auditing the bundles an existing FHIR store already emits. Covers the resource profiles ABDM requires, the Composition rules, and the validator to check against.
type: skill
domain: fhir
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
- fhir-compliance-agent
requires:
- abdm-docs-mcp
produces:
- nrces-document-bundle
can_execute: true
can_orchestrate: false
---
# ABDM FHIR
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-fhir/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
## What this skill covers
- **Design.** What a generator gets wrong before it reaches a profile table. [references/design.md](references/design.md)
- **Generate.** Build NRCES compliant bundle generation into a codebase. [references/generate.md](references/generate.md)
- **Audit.** Check an existing FHIR store's output against the same profiles. [references/audit.md](references/audit.md)
Open one when the work calls for it. This file is the map, not the material.
## Try asking
- "Add ABDM compliant FHIR bundle generation to this codebase"
- "Audit the FHIR bundles this codebase already emits"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- A bundle that validates is not a bundle ABDM accepts. The NRCES profiles are the floor, and the milestone the bundle travels under adds its own rules on top.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.

View File

@ -0,0 +1,91 @@
# fhir-audit
Audit an existing FHIR store's output against ABDM's NRCES profiles,
and fix the generation pipeline where it falls short. The store stays
authoritative: this skill patches what emits from it, it does not
replace it.
## 1. Collect representative output
From the integrator, get one real (or realistic test) document bundle
per record type their store produces, saved as local `.json` files.
Do not synthesize bundles yourself for this step; the point of the
audit is what the store actually emits, not what a correctly built
bundle would look like.
## 2. Tier 1 pass
Run `validate` with `kind: fhir` on each bundle, passing its `record_type`. Collect
every finding across every bundle into one gap report, grouped by
finding (the shape of the defect, for example "`Composition.author`
missing" or "`Composition.meta.profile` does not include ..."), not by
which bundle it came from. The same defect usually repeats across every
bundle the store produces, because it comes from one place in the
pipeline; grouping by bundle would make the same root cause look like
many separate problems.
## 3. Interpret with the knowledge surface
For each finding, use its Ref to read what NRCES actually demands and
why ABDM checks it. Ref names the NRCES profile the rule came from
(for example `OPConsultRecord` or `DocumentBundle`), not an atom id
directly; map it to the record type's mapping atom via `get`:
`shared.fhir.document-bundles` for envelope-level findings (Ref
`DocumentBundle`), or the type's mapping atom
(`shared.fhir.map-opconsultation`, `shared.fhir.map-prescription`,
`shared.fhir.map-diagnosticreport`, `shared.fhir.map-dischargesummary`,
`shared.fhir.map-immunizationrecord`,
`shared.fhir.map-healthdocumentrecord`, or
`shared.fhir.map-wellnessrecord`) for a Composition-element or
section-level finding. Four record types (Prescription,
DiagnosticReport, HealthDocumentRecord, ImmunizationRecord) have no
NRCES section slices: do not expect or ask for section-by-section
findings on these four, only Composition-element and entry-level ones.
Sort each finding into one of two buckets:
- **Generation-time fix.** The store already captures the data; the
pipeline just emits the wrong shape (wrong or missing
`meta.profile`, a malformed reference, a missing `Bundle.identifier`
field). This is fixable in code, in this session.
- **Mapping gap.** The store never captures the data a required
element needs at all (no encounter record to populate
`Composition.encounter`, no coded allergy list to populate an
`AllergyIntolerance` entry). This is not a code fix; it is a
conversation with the integrator about what their store needs to
start capturing, and it belongs in the gap report as an open item,
not a silent workaround.
## 4. Patch, do not replace
For every generation-time fix, amend the integrator's existing
generation pipeline directly, or add a thin adapter that post-processes
the store's output into the compliant shape if the pipeline itself is
not reasonably editable. Either way the store keeps producing its own
output; this skill's job is the layer between that output and what
ABDM requires, not a replacement generator.
## 5. Tier 2 pass
Follow `shared.fhir.hl7-validator-recipe` against the patched output
for each record type: download the pinned `validator_cli.jar`, run it
with `-ig ndhm.in#6.5.0` (the recipe atom is the authoritative source
for the pinned version, defer to it if this ever looks stale), and
iterate the same way as tier 1, fix, re-run, until the report lists no
errors. A warning is not the same as a pass; read each one and decide
deliberately whether it is acceptable.
## 6. Done means
The gap report shows every original finding either resolved (with the
fix that resolved it) or explicitly accepted by the integrator (mapping
gaps they have chosen to defer, named as such, not silently dropped).
Tier 1 and tier 2 are both clean against every collected bundle, and
the exact commands to re-run both checks are recorded in the
integrator's repo (a make target or script) so the checks outlive this
session.
Never invent clinical codes: where the integrator's data lacks a coded
value, surface the gap to the human rather than fabricating a SNOMED
code (tier 1 will not catch a wrong code; tier 2 may not either without
a terminology server).

View File

@ -0,0 +1,115 @@
# fhir-generate
Scaffold FHIR document bundle generation for one or more ABDM record
types, directly into the integrator's own codebase, and drive it to a
clean bill of health against both validation tiers.
## 1. Establish scope
Call `search` with `kind: fhir_profile` to get the full list of ABDM record types
(the seven `hiType` values: OPConsultation, Prescription,
DiagnosticReport, DischargeSummary, ImmunizationRecord,
HealthDocumentRecord, WellnessRecord). Confirm with the integrator
which of these seven their system must produce; do not build all seven
speculatively if only one or two are needed.
Identify the language and the repository (or the path within a
monorepo) where the generation module will live. If the integrator
already has a FHIR-adjacent module (a `fhir/`, `hie-cm/`, or similar
directory), that is where this work belongs.
## 2. Learn the target shape
For each in-scope record type:
- Call `get` with `fhir:<that type>`, and once with `fhir:DocumentBundle`
(the shared envelope profile every record type rides on top of).
- Call `get` with `fhir-example:<that type>`, to see a concrete, valid
bundle.
- Read `shared.fhir.document-bundles` via `get` for the envelope
rules that apply to every record type (`Bundle.type`, `Bundle.meta`,
`Bundle.identifier`, `Bundle.timestamp`).
- Read that type's mapping atom via `get`
(`shared.fhir.map-opconsultation`, `shared.fhir.map-prescription`,
`shared.fhir.map-diagnosticreport`, `shared.fhir.map-dischargesummary`,
`shared.fhir.map-immunizationrecord`,
`shared.fhir.map-healthdocumentrecord`, or
`shared.fhir.map-wellnessrecord`) for the Composition's required
elements and, where NRCES defines them, its section slices.
Four record types (Prescription, DiagnosticReport, HealthDocumentRecord,
ImmunizationRecord) have no NRCES section slices: their Composition
carries one unnamed, required section whose `entry` array is what is
actually constrained. Do not scaffold section-by-section guidance for
these four; scaffold the required entry resource types their mapping
atom names instead. OPConsultation, DischargeSummary and WellnessRecord
do have named section slices; use the mapping atom's section table for
those.
## 3. Scaffold in the integrator's stack
For each in-scope record type, write a builder function that:
- Takes the integrator's own domain data as input (their patient,
encounter, order, or report model, whatever they call it).
- Produces a document bundle matching the digest returned by
`get` with `fhir:<type>`: the correct `meta.profile` URL on the
Composition, every required top-level element the digest lists, and
the section shape the mapping atom describes for that type.
- Resolves every reference internally: any resource a section entry or
the Composition points at must be added to the bundle as its own
entry, not left dangling.
Then write one bundle-level wrapper, shared across all in-scope
builders, that applies the `DocumentBundle` envelope constraints:
`Bundle.type` fixed to `"document"`, `Bundle.meta` and
`Bundle.meta.versionId` set, `Bundle.identifier` (`.system` and
`.value`) set, `Bundle.timestamp` set. The Composition must be the
bundle's first entry.
Follow the repository's existing conventions for module layout, error
handling, and naming. If the integrator's stack already has a FHIR
library (a typed resource model, a bundle builder), build on it rather
than duplicating it. If it has none, plain JSON construction (maps or
structs marshaled to JSON) is fine; do not introduce a new FHIR library
dependency just for this.
## 4. Iterate against tier 1
Run each builder's output bundle through `validate` with `kind: fhir`, passing the
bundle and its `record_type`. For every finding returned:
- Read the finding's Fix text.
- Apply exactly that fix to the builder (not a workaround in the test
data).
- Re-run `validate` with `kind: fhir`.
Repeat until `validate` returns no findings for that record type,
then move to the next in-scope type.
## 5. Finish with tier 2
Tier 1 is structural and fast; it does not check nested cardinalities,
terminology bindings, or section slice discriminators. Follow
`shared.fhir.hl7-validator-recipe` end to end, in the integrator's own
environment: download the pinned `validator_cli.jar`, run it against
each builder's output bundle with `-ig ndhm.in#6.5.0` (the recipe atom
is the authoritative source for the pinned version, defer to it if
this ever looks stale), and resolve every error the report lists. A
warning is not an error; read each one and decide deliberately whether
it is acceptable, rather than treating a warning-only run as clean.
## 6. Done means
For every in-scope record type: tier 1 (`validate`, `kind: fhir`) returns no
findings, and tier 2 (the official HL7 validator) exits 0 with no
errors, against IG 6.5.0. Record the exact commands to re-run both
checks (a make target, an npm script, or a shell script committed to
the integrator's repo) so the checks outlive this session and the next
person who touches this module can re-verify without re-deriving the
commands.
Never invent clinical codes: where the integrator's data lacks a coded
value, surface the gap to the human rather than fabricating a SNOMED
code (tier 1 will not catch a wrong code; tier 2 may not either without
a terminology server).

View File

@ -0,0 +1,79 @@
---
name: abdm-gateway
description: Use when building, debugging or testing the ABDM gateway: the gateway session and bridge registry.
type: skill
domain: gateway
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- sandbox-client-credentials
produces:
- gateway-session-token
- bridge-url
can_execute: true
can_orchestrate: false
---
# ABDM Gateway, sessions and the bridge registry
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-gateway/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with Gateway
- Bridge
- Session
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Get a gateway access token and register this bridge"
- "What has to be in place before my first Gateway call?"
- "My Gateway call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 4 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/gateway
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug Gateway, sessions and the bridge registry
The codes this module can return, with the message and the operation that returns each.
## Errors
The Gateway, sessions and the bridge registry specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,37 @@
# Integrate Gateway, sessions and the bridge registry
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
4 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,208 @@
# HIE-CM gateway build
Scaffolds an ABDM gateway integration one journey at a time. It covers the gateway session and bridge registry.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Bridge (`gateway-abdm-gateway`)
**Act: the calls in this journey, in order**
#### 1. Fetch the service ids registered against a bridge (`gateway_get_gateway_v3_bridge_services`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-services \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
#### 2. Fetch the details of a service ID (`gateway_get_gateway_v3_bridge_service_serviceid_service_id`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
#### 3. Update the bridge URL (`gateway_patch_gateway_v3_bridge_url`)
```bash
curl --request PATCH \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge/url \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"url": "<YOUR_CALLBACK_URL>"
}'
```
**Exit condition (Observe until this is true)**
A 202 response. The specification gives no body for it, so read what comes back.
### Session (`gateway-abdm-sessions`)
**Act: the calls in this journey, in order**
#### 1. Generate access token (`gateway_post_gateway_v3_sessions`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/sessions \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"clientId": "SBX_0000",
"clientSecret": "0******-***-***-***-a****",
"grantType": "client_credentials"
}'
```
**Exit condition (Observe until this is true)**
A 202 whose body matches:
```json
{
"accessToken": "<TOKEN>",
"expiresIn": 1200,
"refreshExpiresIn": 1800,
"refreshToken": "<TOKEN>",
"tokenType": "bearer"
}
```
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/gateway

View File

@ -0,0 +1,102 @@
---
name: abdm-m1
description: Use when building, debugging or testing ABDM Milestone 1: creating an ABHA number or address, ABHA login, profile management, or the gateway session token. Carries the endpoints, the required headers, the two token rule, the encryption rule and the error codes its specification's examples return. Also carries the scaffolding loop that builds it flow by flow and the loop from a failed call to a named fix, in references/.
type: skill
domain: m1
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
produces:
- abha-number
- abha-address
- user-token
- abha-profile
can_execute: true
can_orchestrate: false
---
# ABDM M1, create and verify ABHA
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m1/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M1
- Create an ABHA for somebody who has none: by Aadhaar OTP, by demographic authentication, by face or fingerprint, or under a parent for a child.
- Log in somebody who already has one, by Aadhaar, mobile, ABHA number or ABHA address.
- Read their profile, which carries the whole registration form: names, date of birth, gender, mobile, address with its codes, and a photograph.
- Find an ABHA somebody has forgotten, and pick the right account when one mobile holds several.
- Show the ABHA card and QR code, and take a profile a patient shares by QR at your counter.
- Update a profile and change a mobile.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Add ABHA creation by Aadhaar OTP to this codebase"
- "What has to be in place before my first M1 call?"
- "My M1 call returned ABDM-1017. What is wrong, and how do I fix it?"
- "Walk me through the M1 test cases before go-live"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 123 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 17 error codes from the specification's examples. [references/debug.md](references/debug.md)
- **Test.** The functional test cases, one loop each, with the evidence the sandbox can vouch for and the manifest that replaces a screenshot report. [references/test.md](references/test.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- ABDM publishes operations, not a user experience. The journey is the integrator's to design, so offer the shape below as a suggestion and build what they ask for instead when they have a view of their own counter.
- The ABHA step comes before the registration form and fills it. A verified profile carries the whole form, so a journey that registers the patient first and offers ABHA afterwards has already spent the keystrokes it existed to save.
- Two ways the profile reaches the desk. The patient scans a QR and consents in their own app, and ABDM posts the profile to your callback, so nobody types or asks anything. Or the desk runs the identifier journey. The filled form is the destination either way.
- Decide deliberately whether a journey can complete without an ABHA. A record keyed by the facility's own number does not need one, and a journey that cannot finish without one blocks care for anyone who has none.
- An identifier and an auth method are two different questions, and listing them together is what makes M1 look like five choices. Ask which identifier first, then offer auth methods underneath it, and only where there is more than one.
- Every identifier starts on the login path, Aadhaar included. A verified login already returns the accounts, and only ABDM-1114 on a refusal means nobody holds one. Create on any other answer and the patient ends up with two numbers, which nothing merges.
- Get an access token first, from the gateway session endpoint. Every other call needs it in `Authorization: Bearer <token>`.
- Two tokens exist and they are not interchangeable. The gateway access token goes in `Authorization`. The user token from an enrolment or a login goes in `X-token`. Profile endpoints need both.
- Sensitive fields travel encrypted. Aadhaar numbers, mobile numbers, email addresses, OTP values and passwords are encrypted before they go in the body, then base64 encoded.
- The certificate response tells you the padding. `GET /v3/profile/public/certificate` returns `{"publicKey", "encryptionAlgorithm"}`. Read that field and translate it for your language. Do not hard code a padding: a constant is right until it is not, and the failure then looks like a bad value rather than a stale constant.
- One path serves several jobs. The `scope` array in the body picks which one, so read it before assuming an endpoint does one thing.
- The accounts array on a login verification already carries ABHANumber, preferredAbhaAddress, name, gender, dob, profilePhoto and kycVerified, so a registration form can fill the moment the OTP verifies and before any profile call.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m1
- The flows as diagrams: /docs/hiecm/v3/milestones/m1
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,139 @@
# HIE-CM m1 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### 404
**Specification example:** HTTP 404, `Runtime Error`, on `m1_post_v3_profile_login_verify_find_abha_face`.
**Exit condition: the original call now succeeds.**
### 900900
**Specification example:** HTTP 500, `Unclassified Authentication Failure`, on `m1_post_v3_enrollment_auth_byabdm_mobile_verify_create_ab_091285`.
**Exit condition: the original call now succeeds.**
### 900901
**Specification example:** HTTP 401, `Invalid Credentials`, on `m1_post_v3_enrollment_request_otp_aadhaar_otp`.
**Exit condition: the original call now succeeds.**
### 900902
**Specification example:** HTTP 401, `Missing Credentials`, on `m1_post_v3_profile_benefit_search_xmluid`.
**Exit condition: the original call now succeeds.**
### ABDM-1017
**Specification example:** HTTP 401, `Invalid Transaction Id`, on `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp`.
**Exit condition: the original call now succeeds.**
### ABDM-1021
**Specification example:** HTTP 401, `Lack of required priviledges`, on `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1094
**Specification example:** HTTP 401, `X-token expired`, on `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1114
**Specification example:** HTTP 404, `User not found`, on `m1_patch_v3_profile_account_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1124
**Specification example:** HTTP 422, `The mobile number provided by you is already linked to 6 ABHA Numbers. Please provide a different Mobile Number.`, on `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d`.
**Exit condition: the original call now succeeds.**
### ABDM-1138
**Specification example:** HTTP 400, `The benefit record has already been de-linked`, on `m1_post_v3_profile_benefit_linkanddelink_x_token`.
**Exit condition: the original call now succeeds.**
### ABDM-1140
**Specification example:** HTTP 400, `The benefit record has already been linked`, on `m1_post_v3_profile_benefit_linkanddelink_x_token`.
**Exit condition: the original call now succeeds.**
### ABDM-1157
**Specification example:** HTTP 422, `Child ABHA’s account limit has been exceeded for the requested Abha ID number ‘<ABHA_NUMBER>`, on `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1160
**Specification example:** HTTP 422, `Non KYC CHILD ABHA is allowed to update their profile only once`, on `m1_patch_v3_profile_account_child_child_abha`.
**Exit condition: the original call now succeeds.**
### ABDM-1204
**Specification example:** HTTP 422, `UIDAI Error code : 400 : Invalid Aadhaar OTP value.`, on `m1_post_v3_enrollment_enrol_byaadhaar_otp`.
**Exit condition: the original call now succeeds.**
### ABDM-1207
**Specification example:** HTTP 422, `The information you provided does not match the details on record with Aadhaar. Please verify and provide accurate information.`, on `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d`.
**Exit condition: the original call now succeeds.**
### ABDM-1211
**Specification example:** HTTP 400, `User not found.`, on `m1_post_v3_phr_web_login_abha_search_abha_address_login_m_27a20f`.
**Exit condition: the original call now succeeds.**
### ABDM-1224
**Specification example:** HTTP 401, `Login via Biometric is not allowed.`, on `m1_post_v3_profile_login_request_otp_find_abha_face`.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/m1
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `404` | 404 | Runtime Error | `m1_post_v3_profile_login_verify_find_abha_face` |
| `900900` | 500 | Unclassified Authentication Failure | `m1_post_v3_enrollment_auth_byabdm_mobile_verify_create_ab_091285` |
| `900901` | 401 | Invalid Credentials | `m1_post_v3_enrollment_request_otp_aadhaar_otp` |
| `900902` | 401 | Missing Credentials | `m1_post_v3_profile_benefit_search_xmluid` |
| `ABDM-1017` | 401 | Invalid Transaction Id | `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp` |
| `ABDM-1021` | 401 | Lack of required priviledges | `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha` |
| `ABDM-1094` | 401 | X-token expired | `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha` |
| `ABDM-1114` | 404 | User not found | `m1_patch_v3_profile_account_child_child_abha` |
| `ABDM-1124` | 422 | The mobile number provided by you is already linked to 6 ABHA Numbers. Please provide a different Mobile Numb… | `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d` |
| `ABDM-1138` | 400 | The benefit record has already been de-linked | `m1_post_v3_profile_benefit_linkanddelink_x_token` |
| `ABDM-1140` | 400 | The benefit record has already been linked | `m1_post_v3_profile_benefit_linkanddelink_x_token` |
| `ABDM-1157` | 422 | Child ABHA’s account limit has been exceeded for the requested Abha ID number ‘<ABHA_NUMBER> | `m1_post_v3_enrollment_enrol_byaadhaar_child_child_abha` |
| `ABDM-1160` | 422 | Non KYC CHILD ABHA is allowed to update their profile only once | `m1_patch_v3_profile_account_child_child_abha` |
| `ABDM-1204` | 422 | UIDAI Error code : 400 : Invalid Aadhaar OTP value. | `m1_post_v3_enrollment_enrol_byaadhaar_otp` |
| `ABDM-1207` | 422 | The information you provided does not match the details on record with Aadhaar. Please verify and provide acc… | `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d` |
| `ABDM-1211` | 400 | User not found. | `m1_post_v3_phr_web_login_abha_search_abha_address_login_m_27a20f` |
| `ABDM-1224` | 401 | Login via Biometric is not allowed. | `m1_post_v3_profile_login_request_otp_find_abha_face` |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
# Integrate M1, create and verify ABHA
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://abhasbx.abdm.gov.in` ABHA service, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
123 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/abha/api/v3.1/profile/login/verify` | Login via Face Auth (QR) - verify |
| `POST` | `/abha/api/v3.1/profile/login/verify` | Login via Biometric (Fingerprint) - v3.1 single step |
| `POST` | `/abha/api/v3.1/profile/login/verify` | Login via Biometric (Iris) - v3.1 single step |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify email OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/auth/byAbdm` | After ABHA creation - verify mobile OTP (optional) |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/abha-address` | After ABHA creation - create ABHA address |
| `POST` | `/abha/api/v3/enrollment/enrol/auth/init` | Face auth - generate transaction ID (init) |
| `POST` | `/abha/api/v3/enrollment/enrol/auth/init` | Face auth - generate transaction ID (init) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create Child ABHA |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create Child ABHA |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Demographic authentication (Demo Auth) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Demographic authentication (Demo Auth) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Aadhaar face authentication |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Aadhaar fingerprint (bio) |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - Aadhaar IRIS |
| `POST` | `/abha/api/v3/enrollment/enrol/byAadhaar` | Create ABHA - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/enrollment/enrol/capturePID` | Face auth - capture PID / track status |
| `POST` | `/abha/api/v3/enrollment/enrol/capturePID` | Face auth - capture PID / track status |
| `POST` | `/abha/api/v3/enrollment/enrol/capturePID` | Face auth - capture PID / track status |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/enrol/suggestion` | After ABHA creation - get ABHA address suggestions |
| `GET` | `/abha/api/v3/enrollment/profile/children` | Get Child ABHA list of the parent |
| `GET` | `/abha/api/v3/enrollment/profile/children` | Get Child ABHA list of the parent |
| `POST` | `/abha/api/v3/enrollment/request/otp` | Send Aadhaar OTP for ABHA enrolment |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify email (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/enrollment/request/otp` | After ABHA creation - send OTP to verify mobile (optional) |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via Aadhaar OTP - send OTP |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via Fingerprint - send authentication request |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via IRIS - send authentication request |
| `POST` | `/abha/api/v3/phr/web/login/abha/request/otp` | ABHA address login via Mobile OTP - send OTP |
| `POST` | `/abha/api/v3/phr/web/login/abha/search` | Search ABHA address (auth methods) |
| `POST` | `/abha/api/v3/phr/web/login/abha/search` | Search ABHA address (auth methods) |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via Aadhaar OTP - verify |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via Fingerprint - verify |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via IRIS - verify |
| `POST` | `/abha/api/v3/phr/web/login/abha/verify` | ABHA address login via Mobile OTP - verify |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha-profile` | Get ABHA-address profile |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/phr-card` | Get PHR card |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/phr/web/login/profile/abha/qr-code` | Get ABHA-address QR code |
| `GET` | `/abha/api/v3/profile/account` | Get ABHA profile |
| `PATCH` | `/abha/api/v3/profile/account` | Update Child ABHA profile |
| `PATCH` | `/abha/api/v3/profile/account` | Update Child ABHA profile |
| `GET` | `/abha/api/v3/profile/account/abha-card` | Retrieve ABHA card image |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `POST` | `/abha/api/v3/profile/account/abha/search` | Search ABHA by mobile |
| `GET` | `/abha/api/v3/profile/account/download-abha-card` | Download ABHA card |
| `GET` | `/abha/api/v3/profile/account/qrCode` | Get ABHA QR code |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Child ABHA KYC - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Child ABHA KYC - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Re-KYC - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/request/otp` | Update mobile - send OTP |
| `GET` | `/abha/api/v3/profile/account/request/token` | Refresh user token |
| `POST` | `/abha/api/v3/profile/account/verify` | Child ABHA KYC - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/verify` | Child ABHA KYC - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/verify` | Re-KYC - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/account/verify` | Update mobile - verify OTP |
| `GET` | `/abha/api/v3/profile/benefit/abha/{abhanumber}` | Get benefits linked to an ABHA number |
| `POST` | `/abha/api/v3/profile/benefit/linkAndDelink` | Link / De-link benefit using ABHA number |
| `POST` | `/abha/api/v3/profile/benefit/linkAndDelink` | Link / De-link benefit using X-token |
| `POST` | `/abha/api/v3/profile/benefit/linkAndDelink` | Link / De-link benefit using xmlUid |
| `POST` | `/abha/api/v3/profile/benefit/search` | Benefit search by ABHA number |
| `POST` | `/abha/api/v3/profile/benefit/search` | Benefit search by xmlUid |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Aadhaar number - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via ABHA number - send Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via ABHA number - send ABHA (mobile) OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Biometric (Fingerprint) - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Biometric (Iris) - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Aadhaar OTP - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Face - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Fingerprint - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via IRIS - send authentication request |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Find ABHA via Mobile - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Forgot ABHA via Aadhaar - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Forgot ABHA via Mobile - send OTP |
| `POST` | `/abha/api/v3/profile/login/request/otp` | Login via Mobile number - send OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Aadhaar number - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via ABHA number - verify Aadhaar OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via ABHA number - verify ABHA (mobile) OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Biometric (Fingerprint) - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Biometric (Iris) - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Aadhaar OTP - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Face - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Fingerprint - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via IRIS - verify |
| `POST` | `/abha/api/v3/profile/login/verify` | Find ABHA via Mobile - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Forgot ABHA via Aadhaar - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Forgot ABHA via Mobile - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify` | Login via Mobile number - verify OTP |
| `POST` | `/abha/api/v3/profile/login/verify/user` | Login via Mobile number - verify user (select ABHA) |
| `GET` | `/abha/api/v3/profile/public/certificate` | Get public certificate (RSA encryption key) |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for every request. |
| `TIMESTAMP` | Current UTC timestamp in ISO-8601 format. |
| `BENEFIT_NAME` | **Applicable for user who is enrolling via Benefit Program.** |
| `X-token` | **Applicable for child abha creation. X-token of Parent user, user can get X-token after login to the system** |
| `TRANSACTION_ID` | Transaction ID from the ABHA creation response. |
| `Content-Type` | `application/json`. |
| `R-token` | Refresh token (`Bearer <refreshToken>`) received at login. |
| `T-token` | Short-lived token (`Bearer <token>`) returned by *Login via Mobile number - verify OTP*. |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://abhasbx.abdm.gov.in/abha/api/v3.1/profile/login/verify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'Content-Type: application/json' \
--data '{
"scope": [
"abha-login",
"aadhaar-face-login-verify"
],
"authData": {
"authMethods": [
"face_auth"
],
"face_login": {
"txnId": "8220299c-40ad-40b6-bc52-13a7d46a69d0",
"aadhaar": "{{encrypted aadhaar number}}"
}
}
}'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,401 @@
# HIE-CM m1 test
Walks the ABDM M1 functional test cases, ABHA creation and verification, against the system you built and the sandbox. Each case ends in one of four recorded outcomes, never in an opinion.
## What this skill does
- Runs every M1 test case in the ABHA creation and verification set against your system and the sandbox, one case at a time
- Records, for each case, the sandbox response and REQUEST-ID that prove it, or the screen that has to be attested, or the human step it stopped at
- Writes a manifest of case ids to request ids at the end, which is the evidence a reviewer checks against the gateway rather than a folder of screenshots
## What it needs from you
| Need | Where it comes from |
|---|---|
| Sandbox client id and secret | The ABDM sandbox portal, under your registered application |
| Your system running against the sandbox hosts | Your deployment or a local build. The skill drives your system, not the sandbox directly |
| A way to trigger each journey in your system | A screen, an API or a script. Say which at the start |
| A person with an Aadhaar linked mobile, for OTP cases | Present at the desk for the run. Every OTP is a human step and the skill stops for it |
| A fingerprint or face device, for the biometric cases | Only if you claim those cases. Without one they are recorded as not run |
| Whether you are a government or a private integrator | The set marks some cases mandatory for one and optional for the other |
## Say one of these
- "Walk the M1 test cases against my sandbox and log each one."
- "Run only the ABHA verification cases, VRFY_ABHA_101 to 405."
- "Re-run CRT_ABHA_112, the address suggestion case failed yesterday."
- "Write a terminal script that runs the M1 test cases and asks me for the Aadhaar number and each OTP."
## What happens first
One question: which cases. All of them, one function group, or a list of ids. Then the preconditions for the first case are checked before anything is sent. Nothing else is asked until a case needs a human.
## How this skill runs
Every case is an observe, orient, decide, act loop. Observe the live state: the last response, the last REQUEST-ID, what your screen shows. Orient against the case below. Decide the cheapest action that produces a new observation. Act, paste the raw result, return to observe. A case is done only when its exit condition is observed, never because the step should have worked.
Loop limit: 3 passes per test case. Hitting the limit is an escalation: state what was observed with request ids, what was tried, name one section of this skill or one operation page to read, and ask one question.
### The four outcomes
| Outcome | Record it when | Never when |
|---|---|---|
| `passed` | The exit condition is observed and the observation is pasted verbatim | The response was not pasted. A pass with no output is a fail |
| `failed` | Three passes and the exit condition was not observed, or the sandbox returned the refusal the case expects you to handle and your system did not handle it | A human step was missing. That is `needs-human` |
| `needs-human` | The case reached an OTP, a biometric scan, an operator's document check, or a person who was not there | Ever as a substitute for a fail you can see |
| `not-run` | The case does not apply to your entity, you do not claim the optional capability, or ABDM publishes no operation for it | To skip a mandatory case that applies to you |
### The three kinds of evidence
Every case names one of these. It decides what the manifest carries.
- **sandbox.** A call the gateway sees. Evidence is the HTTP status, the body and the REQUEST-ID, pasted. The exit condition is a literal in the body. A reviewer can check this against the gateway's own record, which is why it outranks the other two.
- **screen.** A rule about what your system shows or stores, which no call proves. Evidence is one screenshot or a short recording and the design rule it satisfies. A screen case is attested, not observed, and the manifest marks it so.
- **human.** A step only a person can do. Evidence is the observation just before it and the outcome `needs-human` when nobody was there.
### Rules that hold for every case
- Search before create. Every identifier goes to the login path first, and only ABDM-1114 on the verify says nobody holds an account. Creating on any other answer gives the patient two numbers, which nothing merges. `hiecm.concept.m1-avoiding-duplicate-abha`
- Never ask twice. A value the transaction already holds, the Aadhaar number, the mobile, the txnId, is carried, not re-entered. `hiecm.concept.m1-never-ask-twice`
- Two tokens. The gateway access token goes in `Authorization`. The user token an enrolment or a login returns goes in `X-token`. Profile calls need both.
- Fresh `REQUEST-ID` on every call, logged before sending. A reused id is refused, and after a failure the id is the only handle on the call.
- Paste the response. A step with no pasted output is not done.
- Sensitive values travel encrypted: Aadhaar, mobile, email, OTP. Read the algorithm from the certificate call rather than hard coding a padding.
- One question when you escalate. The person is being interrupted.
### The run record
Append one block per loop pass. It is the raw material for the manifest.
```text
case: CRT_ABHA_101 pass: 1 of 3 at: 2026-09-29T10:14:02Z
observed: POST /abha/api/v3/enrollment/enrol/byAadhaar REQUEST-ID 6f1c... HTTP 200
body: {"message":"Account created successfully","txnId":"...","tokens":{"token":"<X-token>",...},"ABHAProfile":{"ABHANumber":"91-...",...}}
matched: exit condition for CRT_ABHA_101, step 2
next: CRT_ABHA_113
```
### When the run is a script
Asked for a test script, a test command or automated tests, build one interactive terminal runner that walks the cases below. It does the typing. Every rule above still holds.
| Rule | Why |
|---|---|
| Name every check by its case id from this skill, such as `CRT_ABHA_101`. Never invent a numbering such as `TC01` | The functional testing report quotes these ids. A check with no case id answers nothing a reviewer asks |
| At each `human` step, stop and prompt in the terminal for the value: Aadhaar number, mobile, OTP, the chosen address. Read it from stdin, encrypt it, send it | Every M1 success path passes through an OTP. A runner that never asks never reaches one |
| Never write a typed value to disk, a log or the manifest, and never hard code one | An Aadhaar number and an OTP belong to the person, not the run |
| Run each `sandbox` case's success path and the refusal its exit condition names, as separate checks | The case passes only when both are observed |
| Compare the status and the body literal the exit condition names, such as `200` with `tokens.token` present, or `422` with `error.code` `ABDM-1204` | A status alone never passes a check |
| A `4xx` passes only where the case's exit condition names that refusal | A `400` on a success path step is `failed` |
| A blank answer at a prompt records the case `needs-human` and moves to the next case | Nobody present is neither a pass nor a fail |
| A `screen` case prints its check, then asks `passed? y/n` and the evidence file name | No call proves a screen, so the case is attested |
| At the end, print one line per case id with its outcome and request ids, then write the manifest in the last section | The person reads outcomes by case id, not by script step |
A run reads like this. The person's values are typed at the prompts and never echoed back.
```text
CRT_ABHA_105 POST /v3/enrollment/request/otp REQUEST-ID 3b9e... HTTP 200 txnId present matched
Enter the OTP sent to the mobile ending 1234, or leave blank if nobody is here:
CRT_ABHA_107 wrong OTP REQUEST-ID 7c02... HTTP 422 error.code ABDM-1204 matched
CRT_ABHA_107 right OTP REQUEST-ID 9a41... HTTP 200 tokens.token present matched
CRT_ABHA_107 passed
```
Nudge: calls with a bad `scope`, a bad `loginHint` or a made up `txnId` test your client, not a case. Run them first if you want them, label them `preflight`, and keep them out of the case counts.
## Test cases
The ids, the mandatory marks and the wording of each check are ABDM's, from the M1 ABHA creation and verification set. The sections are grouped as the set groups them. Where the set names an operation, the operation id here is the one in `references/integrate.md`, which carries the host, the headers and a full request.
### CRT_ABHA_101 to 115: create an ABHA by Aadhaar OTP
Applies to every integrator. Mandatory unless marked. This is the group every other creation group repeats with a different authentication step, so it is written in full and the others state only what differs.
Loop limit: 3 passes per test case.
**Preconditions.** A gateway access token less than 30 minutes old. A person present with an Aadhaar linked mobile. Their Aadhaar number searched first on the login path and not found, so the run creates rather than duplicates.
#### CRT_ABHA_101, the option exists. `sandbox`
Your system offers ABHA creation by Aadhaar OTP, and the journey completes. Drive it end to end through your screen or API.
Calls, in order: `m1_post_v3_enrollment_request_otp_aadhaar_otp`, `m1_post_v3_enrollment_enrol_byaadhaar_otp`, the optional mobile and email verifications, `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp`, `m1_post_v3_enrollment_enrol_abha_address_create_abha_aadhaar_otp`, then `m1_get_v3_profile_account` and `m1_get_v3_profile_account_abha_card`.
Exit condition: three literals, all pasted.
1. `m1_post_v3_enrollment_enrol_byaadhaar_otp` returns 200 with `tokens.token` present and `ABHAProfile.ABHANumber` present.
2. `m1_post_v3_enrollment_enrol_abha_address_create_abha_aadhaar_otp` returns 200 with `healthIdNumber` and `preferredAbhaAddress`.
3. `m1_get_v3_profile_account`, sent with that `X-token`, returns 200 with the same `ABHANumber`.
Nudge: read `message` on step 1. `Account created successfully` is a new account. `This account already exist` is a 200 too, and it means the search-first rule was skipped or the sandbox person already holds one. Record which, because the second is a screen failure on CRT_ABHA_101 even though the API passed.
#### CRT_ABHA_102, consent is shown and recorded. `screen`
Your system shows the published ABDM consent text before the Aadhaar number is asked for, and stores that the person agreed, with when. Evidence: the consent screen, and the stored record for this run. `hiecm.concept.m1-screen-contract`
#### CRT_ABHA_103, consent in another language. `screen`, optional
The same consent offered in at least one language other than English. Evidence: the screen in that language. Record `not-run` if you do not claim it.
#### CRT_ABHA_104, an invalid Aadhaar number is refused before any call. `screen`
Enter an 11 digit and a 13 digit value. Your system refuses each with a message that says the number is not valid, and sends nothing. Evidence: the refusal on screen and an empty call log for that attempt. Your own check has to be the strict one here; the sandbox's refusal for a bad number is not the one this case is about. `hiecm.concept.m1-honest-screen-states`
#### CRT_ABHA_105, the Aadhaar OTP is requested and can be entered. `sandbox`, then `human`
Exit condition: `m1_post_v3_enrollment_request_otp_aadhaar_otp` returns 200 with a `txnId` and a `message` that names the masked mobile. Paste it. Then stop: the OTP arrives on the person's phone and only they enter it. If they are not there, record `needs-human` and the txnId, and do not mark the case passed or failed.
Screen check alongside: the prompt tells the person the OTP went to the mobile linked with Aadhaar and shows the last digits, which the `message` carries.
#### CRT_ABHA_106, resend OTP. `screen`
The resend control is disabled for 60 seconds after each send and works at most twice. Evidence: a recording or three timestamped screenshots: disabled at send, enabled after 60 seconds, disabled after the second resend. A resend that works reuses the `txnId`; a fresh `txnId` is a new request, not a resend.
#### CRT_ABHA_107, the OTP is verified, and a wrong OTP is refused. `sandbox`, `human`
Two observations, both needed.
1. A wrong OTP: `m1_post_v3_enrollment_enrol_byaadhaar_otp` returns 422 with `error.code` `ABDM-1204` and a message naming an invalid Aadhaar OTP value, and your screen says the OTP was wrong and lets the person retry without re-entering the Aadhaar number.
2. The right OTP: the 200 from CRT_ABHA_101 step 1.
The person enters both OTPs. Stop for each.
#### CRT_ABHA_108, the communication mobile is the Aadhaar mobile. `sandbox`, optional
If your system checks whether the mobile the person gave is the Aadhaar linked one and skips the second OTP when it is, drive that path. Exit condition: the journey reaches the ABHA address step with no `mobile-verify` call in the log. Record `not-run` if you always verify, which CRT_ABHA_109 then covers.
#### CRT_ABHA_109, a different communication mobile is verified. `sandbox`, `human`
Give a mobile that is not the Aadhaar linked one. Exit condition: `m1_post_v3_enrollment_request_otp_mobile_verify_create_ab_1b66bb` returns 200 with a `txnId`, the person enters the OTP, and `m1_post_v3_enrollment_auth_byabdm_mobile_verify_create_ab_091285` returns 200. A wrong OTP here is refused on screen and the person can retry.
#### CRT_ABHA_112, the ABHA address is suggested and validated. `sandbox` and `screen`
Mandatory for private and government integrators. Optional for a programme that creates a default address by demographic authentication.
Exit condition: `m1_get_v3_enrollment_enrol_suggestion_create_abha_aadhaar_otp` returns 200 with `abhaAddressList` holding at least three entries, and `m1_post_v3_enrollment_enrol_abha_address_create_abha_aadhaar_otp` returns 200 with `preferredAbhaAddress` equal to the one chosen.
Screen check: at least three suggestions are shown, or a free field with these rules printed beside it, and an address already taken is refused with a message that says so and offers the suggestions.
```text
length 8 to 18
letters, digits, or both
at most one dot and at most one underscore, neither first nor last
```
#### CRT_ABHA_113, the ABHA number is displayed. `screen`
The 14 digit ABHA number and the ABHA address from CRT_ABHA_112 are shown together after creation. Evidence: the screen, with the number matching `healthIdNumber` from the pasted response.
#### CRT_ABHA_114, the ABHA card can be viewed and downloaded. `sandbox` and `screen`
Mandatory for private integrators that generate the card. Exit condition: `m1_get_v3_profile_account_abha_card` returns 200 with content type `image/png`, sent with the `X-token` from creation. Screen check: the card shown carries the ABHA number, the QR code, date of birth, gender and the ABHA address. The photo is optional.
#### CRT_ABHA_115, your own card carries the ABHA details. `screen`
For an integrator that prints its own programme card instead. Mandatory for government where CRT_ABHA_114 is not done. The card shows the ABHA number and address, and your system stores both against your own patient id. Evidence: the card and the stored record.
### CRT_ABHA_201 to 210: create an ABHA by Aadhaar biometric
Applies to every integrator. Optional, except that CRT_ABHA_209 is mandatory for a private integrator that generates the card and one of 209 or 210 is mandatory for government.
Loop limit: 3 passes per test case.
Same cases as 101 to 115 with these differences. Run each as its counterpart above and record the counterpart's evidence.
| Case | Counterpart | What differs |
|---|---|---|
| CRT_ABHA_201 | 101 | The creation call is `m1_post_v3_enrollment_enrol_byaadhaar_fingerprint` or `m1_post_v3_enrollment_enrol_byaadhaar_face`. Exit condition is the same 200 with `tokens.token` and `ABHAProfile.ABHANumber` |
| CRT_ABHA_205 | 105 and 107 | `human`: the person scans. A failed scan is refused on screen and can be retried. Face runs `m1_post_v3_enrollment_enrol_auth_init_create_abha_face_au_4dea33` then the capture call first |
| CRT_ABHA_206, 207 | 108, 109 | Unchanged |
| CRT_ABHA_208 | 113 | Unchanged |
| CRT_ABHA_209, 210 | 114, 115 | Unchanged |
| CRT_ABHA_202, 203, 204 | 102, 103, 104 | Unchanged |
Without a registered device the whole group is `not-run`, with that reason.
### CRT_ABHA_301 to 309: create an ABHA by demographic authentication
Government entities only. Mandatory for them. A private integrator records the group `not-run`.
Loop limit: 3 passes per test case.
#### CRT_ABHA_301 and 305, the demographic call creates the account. `sandbox`
One call, `m1_post_v3_enrollment_enrol_byaadhaar_demo_auth_create_ab_81107d`, with the name, date of birth, gender, Aadhaar number, mobile, address, state and district as they read on the Aadhaar card. Exit condition: 200 with `healthIdNumber`, `healthId` and `kycVerified` `true`.
Then the mismatch: send the same call with the date of birth changed. Exit condition: a 422 whose message says the information does not match the details on record with Aadhaar, and your screen says so without creating anything. Both observations, both pasted.
Nudge: the same mobile on a seventh account is refused with ABDM-1124. That is a mobile limit, not a demographic mismatch. Read `error.message`.
#### CRT_ABHA_305 address, the three cases. `sandbox` and `screen`
The address on the card and the profile is the same one in all three cases: the existing address when the person had one, the address they chose when they chose one, and the default `<ABHA number>@sbx` when neither. Evidence: `healthId` in the creation response and the address on `m1_get_v3_profile_account`, equal, for whichever case the run hit. Record which.
#### CRT_ABHA_306, the profile is filled from the response, not typed. `screen`
Name, date of birth and gender are shown filled and not editable. Address, mobile and email are editable. Evidence: the form after creation. `hiecm.concept.m1-confirm-the-filled-form`
#### CRT_ABHA_302, 303, 304, 307, 308, 309
As 102, 103, 104, 113, 114 and 115.
### CRT_ABHA_401 to 411: create an ABHA by driving licence or PAN
ABDM's M1 specification publishes no enrolment by document operation. Record every case in this group `not-run` with that reason, whatever your entity type, until an operation is published. Do not improvise a call.
### VRFY_ABHA_101 and 102: verify an ABHA number or address by Aadhaar OTP
Applies to every integrator. Mandatory.
Loop limit: 3 passes per test case.
**Preconditions.** A person present with an ABHA and the Aadhaar linked mobile.
#### VRFY_ABHA_101, by ABHA number. `sandbox`, `human`
Calls: `m1_post_v3_profile_login_request_otp_abha_number_aadhaar_otp`, the OTP, `m1_post_v3_profile_login_verify_abha_number_aadhaar_otp`, then `m1_get_v3_profile_account` with the returned token as `X-token`.
Exit condition: the verify returns 200 with `authResult` `success` and `accounts[0].ABHANumber`, and the profile call returns 200 with the same `ABHANumber`. A wrong OTP returns 400 with `otpValue` naming an invalid OTP value, and your screen says the verification failed.
Screen checks alongside: resend as in CRT_ABHA_106; name, date of birth and gender shown not editable; the profile stored against your own patient id after verification.
Nudge: the accounts array already carries the whole registration form. Fill the form from it before the profile call, so the person reads back rather than types.
#### VRFY_ABHA_102, by ABHA address. `sandbox`, `human`
Calls: `m1_post_v3_phr_web_login_abha_search_abha_address_login_a_de8184`, `m1_post_v3_phr_web_login_abha_request_otp_aadhaar_otp`, the OTP, `m1_post_v3_phr_web_login_abha_verify_aadhaar_otp`, then `m1_get_v3_phr_web_login_profile_abha_profile_abha_address_c04e83`.
Exit condition: the verify returns 200 with a token, and the profile call returns 200 carrying the ABHA number and the address searched for. Paste both.
### VRFY_ABHA_201 and 202: verify by the ABHA linked mobile OTP
Mandatory. As VRFY_ABHA_101 and 102 with the mobile OTP operations: `m1_post_v3_profile_login_request_otp_abha_number_abha_otp` and `m1_post_v3_profile_login_verify_abha_number_abha_otp` for the number, `m1_post_v3_phr_web_login_abha_request_otp_mobile_otp` and `m1_post_v3_phr_web_login_abha_verify_mobile_otp` for the address. Same exit conditions, same screen checks.
Loop limit: 3 passes per test case.
### VRFY_ABHA_301 to 305: fetch ABHA details by the communication mobile
Mandatory. This is the lookup that costs no Aadhaar OTP, so it is the one your desk should run first.
Loop limit: 3 passes per test case.
#### VRFY_ABHA_301, an invalid mobile is refused. `screen`
A nine digit and an eleven digit value are refused on screen with a message asking for a valid mobile, and nothing is sent.
#### VRFY_ABHA_302, no ABHA on this mobile. `sandbox`, `human`
Calls: `m1_post_v3_profile_login_request_otp_mobile`, the OTP, `m1_post_v3_profile_login_verify_mobile_otp`.
Exit condition: the verify returns 404 with `error.code` `ABDM-1114`. Your screen says no ABHA number is linked to this mobile, and offers to create one. The create offer is the point of the case; a dead end fails it. `hiecm.concept.m1-journey-completes-without-abha`
#### VRFY_ABHA_303, one or more ABHAs on this mobile. `sandbox`, `human`
Same two calls with a mobile that holds accounts, then `m1_post_v3_profile_login_verify_user` with the chosen `ABHANumber` and the first verify's token as `T-token`, then `m1_get_v3_profile_account`.
Exit condition: the first verify returns 200 with `accounts` holding at least one entry with `ABHANumber`, `preferredAbhaAddress`, `name` and `profilePhoto`; the user verify returns 200 with `token`; the profile call returns 200 with the chosen `ABHANumber`.
Screen checks: every account in the array is listed with name, number, address and photo; choosing one already linked to a patient in your system says so and names your patient id; choosing one not yet linked registers and links it. `hiecm.concept.m1-two-routes-to-the-profile`
Nudge: read the array as plural. One mobile can hold up to six accounts, and a screen that shows the first one silently links the wrong person.
#### VRFY_ABHA_304, a wrong OTP. `sandbox`, `human`
Exit condition: `m1_post_v3_profile_login_verify_mobile_otp` returns 400 with `otpValue` naming an invalid OTP value, and your screen says so and allows a retry without re-entering the mobile.
#### VRFY_ABHA_305, resend. `screen`
As CRT_ABHA_106.
### VRFY_ABHA_401 to 405: fetch ABHA details by Aadhaar number
Mandatory.
Loop limit: 3 passes per test case.
#### VRFY_ABHA_401, an invalid Aadhaar is refused. `screen`
As CRT_ABHA_104, on the verification screen.
#### VRFY_ABHA_403, no ABHA on this Aadhaar. `sandbox`, `human`
Calls: `m1_post_v3_profile_login_request_otp_aadhaar`, the OTP, `m1_post_v3_profile_login_verify_aadhaar_otp`.
Exit condition: the request returns 200 with a `message` naming the masked Aadhaar mobile, the person enters the OTP, and the verify returns 404 with `error.code` `ABDM-1114` and a message that no ABHA user is registered with this Aadhaar number. Your screen says so and offers creation, and the creation path does not ask for the Aadhaar number again. `hiecm.concept.m1-never-ask-twice`
#### VRFY_ABHA_404, an ABHA exists on this Aadhaar. `sandbox`, `human`
Same calls with an Aadhaar that holds an account, then `m1_get_v3_profile_account`.
Exit condition: the verify returns 200 with `authResult` `success` and `accounts[0].ABHANumber`; the profile call returns 200 with the same number. Screen checks: the profile is shown, the card can be downloaded as in CRT_ABHA_114, and the account is registered and linked to your patient id if it was not already.
#### VRFY_ABHA_402 and 405
A wrong OTP and resend, as VRFY_ABHA_304 and 305.
### VRFY_ABHA_501 and 502: verify by fingerprint, government entities
Optional. `human`. Calls: `m1_post_v3_1_profile_login_verify_fingerprint` for a number. The set names no operation for an address by fingerprint; record VRFY_ABHA_502 `not-run` with that reason. Exit condition for 501: 200 with `accounts[0].ABHANumber`, then the profile call as VRFY_ABHA_101. Without a device, `not-run`.
Loop limit: 3 passes per test case.
### Reading the ABHA QR code, optional
The set numbers this row VRFY_ABHA_501 as well. Record it as `QR-read` so the two are told apart in the manifest.
`screen`. No call. Your system scans a person's ABHA QR code and fills name, date of birth, gender and mobile into registration from it. Evidence: the filled form after a scan, with nothing typed. `hiecm.concept.m1-two-routes-to-the-profile`
Loop limit: 3 passes per test case.
### PROF_ABHA_601 to 605: profile update
Optional. Each is `sandbox` and `human`, since every change is confirmed by an OTP.
Loop limit: 3 passes per test case.
| Case | Calls | Exit condition |
|---|---|---|
| PROF_ABHA_601, mobile | `m1_post_v3_profile_account_request_otp_update_mobile`, the OTP, `m1_post_v3_profile_account_verify_update_mobile` | The verify returns 200 with `authResult` `success` and a message that the mobile number was linked; `m1_get_v3_profile_account` then shows the new masked `mobile` |
| PROF_ABHA_602, photo | `m1_patch_v3_profile_account_profile_photo` | 200, and `m1_get_v3_profile_account` returns the new `profilePhoto` |
| PROF_ABHA_603, email | The request and verify calls above with the email scope | 200 on the verify, and the profile shows the new email |
| PROF_ABHA_604, re-KYC | `m1_post_v3_profile_account_request_otp_re_kyc`, the OTP, `m1_post_v3_profile_account_verify_re_kyc` | 200 on the verify, and the profile shows `kycVerified` `true` |
| PROF_ABHA_605, delete or deactivate | ABDM's M1 specification publishes no delete or deactivate operation | `not-run`, with that reason |
### TAGGING_UNIQUEPATIENTID_UNIQUEABHANUMBER: one ABHA per patient id
Mandatory. `screen`. Your system checks whether an ABHA number already exists in its database before it creates a patient id, on both the creation and the verification paths, so one ABHA never maps to two of your patients and one of your patients never holds two ABHAs.
Evidence: verify the same ABHA twice in a run. The second time, the screen says the record is already linked and names your patient id, and the database holds one row. `hiecm.concept.m1-avoiding-duplicate-abha`
Loop limit: 3 passes per test case.
### SHARE_PATIENT_PROFILE_701: share a profile by scanning the facility QR
This case is run from the person's ABHA app, and the call that proves it lands on your side as the `on-share` webhook. It belongs to the scan and register skill, `abdm-scan-and-register`, which carries the webhook and the token reply. Record it there. In this manifest, write `see abdm-scan-and-register`.
## When the last case is recorded
Write the manifest. It is the whole report. Case ids to request ids, nothing else, so a reviewer checks it against the gateway's own record rather than reading screenshots. Secrets never appear in it: no tokens, no OTPs, no Aadhaar numbers, no client secret.
```json
{
"skill": "abdm-m1",
"set": "M1 ABHA creation and verification, NHA, version 1.1",
"entity": "private",
"run_started": "2026-09-29T10:12:40Z",
"run_finished": "2026-09-29T11:48:03Z",
"client_id": "<your sandbox client id>",
"cases": [
{"id": "CRT_ABHA_101", "outcome": "passed", "evidence": "sandbox",
"request_ids": ["6f1c...", "a02d...", "c9e4..."],
"observed": ["byAadhaar 200 message=Account created successfully", "abha-address 200 healthIdNumber", "profile/account 200 ABHANumber"]},
{"id": "CRT_ABHA_102", "outcome": "passed", "evidence": "screen",
"attachment": "consent-2026-09-29.png", "rule": "hiecm.concept.m1-screen-contract"},
{"id": "CRT_ABHA_105", "outcome": "needs-human", "evidence": "human",
"request_ids": ["b7a1..."], "reason": "OTP arrives on the person's phone; nobody present"},
{"id": "CRT_ABHA_401", "outcome": "not-run", "reason": "no enrolment by document operation in the M1 specification"}
]
}
```
Then say, in three lines: how many cases passed, failed, need a human and were not run; which mandatory cases for your entity are not `passed`; and the one case to fix first.

View File

@ -0,0 +1,92 @@
---
name: abdm-m2
description: Use when building, debugging or testing ABDM Milestone 2: care contexts, HIP initiated linking, discovery, and pushing encrypted health records to a requester. Carries the endpoints and the encryption parameters. Also carries the scaffolding loop that builds it flow by flow and the loop from a failed call to a named fix, in references/.
type: skill
domain: m2
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- hip-registration
- callback-url
- nrces-document-bundle
produces:
- care-context
- link-token
- health-information-push
can_execute: true
can_orchestrate: false
---
# ABDM M2, create and link records
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m2/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M2
- Tell ABDM a patient had a visit with you, so their records can be found later.
- Answer a discovery request when somebody looks for that patient.
- Send records out encrypted when a consent says you must.
- Get the link token the linking calls need.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Link a care context for this patient"
- "What has to be in place before my first M2 call?"
- "My M2 call returned ABDM-1000. What is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 24 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 120 error codes from the specification's examples. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- You act as the HIP.
- Before M2, three things must hold: the facility is registered in the HFR with a valid HFR ID, that HIP ID is linked to your client ID (through the NHPR portal or the M4 software linkage APIs), and a callback URL is set for the client ID with the Update Bridge API.
- You are the side that encrypts, and the parameters arrive from the requester rather than from you. The health information request carries `keyMaterial` with `cryptoAlg`, `curve: Curve25519`, the requester's `dhPublicKey` and a `nonce`. Generate your own Curve25519 pair and your own nonce, and send your public key and nonce back with the data so the requester can derive the same secret.
- The key derivation and the symmetric cipher applied over that shared secret are not in the specification. The data flow page (/docs/hiecm/v3/concepts/data-flow) and the Fidelius reference (shared.concept.fidelius-ecdh-interop) give the scheme: HKDF over the shared secret, and AES-GCM for the payload. Build from those, not from a sample.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m2
- The flows as diagrams: /docs/hiecm/v3/milestones/m2
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,860 @@
# HIE-CM m2 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### ABDM-1000
**NHA's list for the module:** `Unable to connect the database`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1001
**NHA's list for the module:** `No data found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1004
**NHA's list for the module:** `SMS Gateway is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Bad Request, invalid request Body`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1007
**NHA's list for the module:** `Connection failed due to timeout`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1008
**NHA's list for the module:** `SMS service currently disabled`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1010
**NHA's list for the module:** `Validation failed`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1011
**NHA's list for the module:** `Gateway database unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1012
**NHA's list for the module:** `No records found against the ABHA Address`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1013
**NHA's list for the module:** `Invalid ABHA Number`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1015
**NHA's list for the module:** `Invalid Response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1016
**NHA's list for the module:** `Invalid TimeStamp`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1017
**NHA's list for the module:** `Invalid TransactionId`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1018
**NHA's list for the module:** `Share Profile database unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1019
**NHA's list for the module:** `Dependent Service Unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1020
**NHA's list for the module:** `Unknown database`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1022
**NHA's list for the module:** `Too many requests`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1023
**NHA's list for the module:** `Invalid User`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1024
**NHA's list for the module:** `Dependent service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1025
**NHA's list for the module:** `Invalid ServiceId`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1026
**NHA's list for the module:** `Invalid Link Token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1027
**NHA's list for the module:** `You are blocked. Please try again after 24 hours.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1028
**NHA's list for the module:** `HIP is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1029
**NHA's list for the module:** `Redis server is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1030
**NHA's list for the module:** `Invalid request ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1031
**NHA's list for the module:** `Invalid request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1032
**NHA's list for the module:** `Invalid header`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1033
**NHA's list for the module:** `HIU is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1034
**NHA's list for the module:** `Notification service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1035
**NHA's list for the module:** `Invalid HIP ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1036
**NHA's list for the module:** `Data does not matched`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1037
**NHA's list for the module:** `Counter and Care context count mismatch`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1038
**NHA's list for the module:** `ABHA address and Link token mismatch`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1040
**NHA's list for the module:** `Invalid HIU ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1041
**NHA's list for the module:** `Invalid Acknowledgement`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1042
**NHA's list for the module:** `Provider Mandatory`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1043
**NHA's list for the module:** `ABHA Address does not match with KYC details.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1044
**NHA's list for the module:** `Broadcast Failed`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1045
**NHA's list for the module:** `Database Access is restricted`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1046
**NHA's list for the module:** `Invalid Purpose`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1047
**NHA's list for the module:** `Purpose does not exist`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1048
**NHA's list for the module:** `Timeout`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1049
**NHA's list for the module:** `Invalid Profile Share Intent Keys`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1050
**NHA's list for the module:** `Invalid Profile Share Metadata Keys`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1051
**NHA's list for the module:** `Invalid ABHA Number or ABHA Address`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1052
**NHA's list for the module:** `Invalid TransactionId or response's requestId`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1401
**NHA's list for the module:** `HIP is not available`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1061
**NHA's list for the module:** `Consent artefact expired`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1062
**NHA's list for the module:** `Consent Not granted`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1063
**NHA's list for the module:** `Date Range given is invalid`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1064
**NHA's list for the module:** `request with this request id already exists`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1109
**NHA's list for the module:** `ABHA DB service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1108
**NHA's list for the module:** `Notification DB service unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1201
**NHA's list for the module:** `IDP Gateway is unavailable`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `Unknown exception`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Invalid combinations of scopes`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1056
**NHA's list for the module:** `This care contexts has been already linked`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1055
**NHA's list for the module:** `Invalid HIP Id or PHR Id`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1056
**NHA's list for the module:** `Invalid Link Reference Number`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1057
**NHA's list for the module:** `Invalid Care Contexts`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1059
**NHA's list for the module:** `Invalid Care Contexts count`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1060
**NHA's list for the module:** `Invalid Patient Reference Number`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1061
**NHA's list for the module:** `Invalid Patient Display`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1062
**NHA's list for the module:** `ABHA number mismatch with Link token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1063
**NHA's list for the module:** `HIP Id mismatch with Link token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1064
**NHA's list for the module:** `Request body was missing`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1065
**NHA's list for the module:** `Invalid X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1066
**NHA's list for the module:** `Invalid JWT token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1067
**NHA's list for the module:** `Request body not required`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1170
**NHA's list for the module:** `Invalid ABHA address`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1084
**NHA's list for the module:** `ABHA address mismatch with X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1085
**NHA's list for the module:** `ABHA number mismatch with X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1086
**NHA's list for the module:** `Patient profile mismatch with X Auth token`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1087
**NHA's list for the module:** `Duplicate patient share request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1090
**NHA's list for the module:** `Duplicate HIP link request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1091
**NHA's list for the module:** `Duplicate Get links request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1092
**NHA's list for the module:** `Duplicate Link token request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1093
**NHA's list for the module:** `Duplicate Bridge request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1094
**NHA's list for the module:** `Duplicate bridge patch request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1095
**NHA's list for the module:** `Duplicate Bridge service request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1102
**NHA's list for the module:** `Profile information cannot be null`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1103
**NHA's list for the module:** `Duplicate Discovery request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1104
**NHA's list for the module:** `Duplicate Init request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1105
**NHA's list for the module:** `Duplicate Confirm request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1106
**NHA's list for the module:** `Duplicate On discovery request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1107
**NHA's list for the module:** `Duplicate On init request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1108
**NHA's list for the module:** `Duplicate On confirm request`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1109
**NHA's list for the module:** `Invalid On discovery response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1110
**NHA's list for the module:** `Invalid On init response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1111
**NHA's list for the module:** `Invalid On confirm response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1112
**NHA's list for the module:** `Invalid or already expired consent artefact id`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1113
**NHA's list for the module:** `Duplicate health information provider data flow response`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1149
**NHA's list for the module:** `Intent type is not supported at HIP end`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1150
**NHA's list for the module:** `Bridge API version cannot be null`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1402
**NHA's list for the module:** `Acknowledgement is not received from HIP`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `HIP is unable to generate a token at this time. Please try again later.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `HIP is unable to process at this time. Please try again later.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1030
**NHA's list for the module:** `Request id not found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2500
**NHA's list for the module:** `No mapping found for`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2401
**NHA's list for the module:** `The X Auth token is invalid.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2402
**NHA's list for the module:** `Invalid Timestamp`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2404
**NHA's list for the module:** `Invalid Request Id`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2429
**NHA's list for the module:** `Too many requests found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2501
**NHA's list for the module:** `Payment status should be : SUCCESS,CANCELED,PENDING,FAIL,REFUND_INITIATED,REFUND_SUCCESS`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2500
**NHA's list for the module:** `Authorization header is missing`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2403
**NHA's list for the module:** `Invalid X-CM-ID`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2406
**NHA's list for the module:** `Invalid API sequence flow, please follow logical flow`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-2406
**NHA's list for the module:** `The status is invalid. Please follow the logical status flow or transition.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `User not found`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**NHA's list for the module:** `Cannot process the request at the moment, please try later.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9001
**NHA's list for the module:** `No open order against ABHA. Please ensure a minimum of one open order`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9002
**NHA's list for the module:** `No registration found at <<hospital name>>. Contact counter support`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9003
**NHA's list for the module:** `Hospital services temporarily unavailable. Please try again after some time.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9004
**NHA's list for the module:** `Services disrupted, please try again.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9005
**NHA's list for the module:** `Bank server not responding. Please try again later`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9006
**NHA's list for the module:** `Service details mismatch. Please ensure original service ID from HMIS`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-9007
**NHA's list for the module:** `The Scan and Pay functionality is not enabled at this facility. Kindly contact the hospital administration.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Invalid count, must be 2 digit and ranges between 1 to 20`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1407
**NHA's list for the module:** `The ABHA Number associated with this ABHA Address is currently deactivated. Please reactivate it.`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
### ABDM-1006
**NHA's list for the module:** `Invalid HIType, it must be in Prescription,DiagnosticReport,OPConsultation,DischargeSummary,ImmunizationRecord,HealthDocumentRecord,WellnessRecord,Invoice`. NHA names no HTTP status and no call for it.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/m2
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `ABDM-1000` | | Unable to connect the database | NHA's list for the module |
| `ABDM-1001` | | No data found | NHA's list for the module |
| `ABDM-1004` | | SMS Gateway is unavailable | NHA's list for the module |
| `ABDM-1006` | | Bad Request, invalid request Body | NHA's list for the module |
| `ABDM-1007` | | Connection failed due to timeout | NHA's list for the module |
| `ABDM-1008` | | SMS service currently disabled | NHA's list for the module |
| `ABDM-1010` | | Validation failed | NHA's list for the module |
| `ABDM-1011` | | Gateway database unavailable | NHA's list for the module |
| `ABDM-1012` | | No records found against the ABHA Address | NHA's list for the module |
| `ABDM-1013` | | Invalid ABHA Number | NHA's list for the module |
| `ABDM-1015` | | Invalid Response | NHA's list for the module |
| `ABDM-1016` | | Invalid TimeStamp | NHA's list for the module |
| `ABDM-1017` | | Invalid TransactionId | NHA's list for the module |
| `ABDM-1018` | | Share Profile database unavailable | NHA's list for the module |
| `ABDM-1019` | | Dependent Service Unavailable | NHA's list for the module |
| `ABDM-1020` | | Unknown database | NHA's list for the module |
| `ABDM-1022` | | Too many requests | NHA's list for the module |
| `ABDM-1023` | | Invalid User | NHA's list for the module |
| `ABDM-1024` | | Dependent service unavailable | NHA's list for the module |
| `ABDM-1025` | | Invalid ServiceId | NHA's list for the module |
| `ABDM-1026` | | Invalid Link Token | NHA's list for the module |
| `ABDM-1027` | | You are blocked. Please try again after 24 hours. | NHA's list for the module |
| `ABDM-1028` | | HIP is unavailable | NHA's list for the module |
| `ABDM-1029` | | Redis server is unavailable | NHA's list for the module |
| `ABDM-1030` | | Invalid request ID | NHA's list for the module |
| `ABDM-1031` | | Invalid request | NHA's list for the module |
| `ABDM-1032` | | Invalid header | NHA's list for the module |
| `ABDM-1033` | | HIU is unavailable | NHA's list for the module |
| `ABDM-1034` | | Notification service unavailable | NHA's list for the module |
| `ABDM-1035` | | Invalid HIP ID | NHA's list for the module |
| `ABDM-1036` | | Data does not matched | NHA's list for the module |
| `ABDM-1037` | | Counter and Care context count mismatch | NHA's list for the module |
| `ABDM-1038` | | ABHA address and Link token mismatch | NHA's list for the module |
| `ABDM-1040` | | Invalid HIU ID | NHA's list for the module |
| `ABDM-1041` | | Invalid Acknowledgement | NHA's list for the module |
| `ABDM-1042` | | Provider Mandatory | NHA's list for the module |
| `ABDM-1043` | | ABHA Address does not match with KYC details. | NHA's list for the module |
| `ABDM-1044` | | Broadcast Failed | NHA's list for the module |
| `ABDM-1045` | | Database Access is restricted | NHA's list for the module |
| `ABDM-1046` | | Invalid Purpose | NHA's list for the module |
| `ABDM-1047` | | Purpose does not exist | NHA's list for the module |
| `ABDM-1048` | | Timeout | NHA's list for the module |
| `ABDM-1049` | | Invalid Profile Share Intent Keys | NHA's list for the module |
| `ABDM-1050` | | Invalid Profile Share Metadata Keys | NHA's list for the module |
| `ABDM-1051` | | Invalid ABHA Number or ABHA Address | NHA's list for the module |
| `ABDM-1052` | | Invalid TransactionId or response's requestId | NHA's list for the module |
| `ABDM-1401` | | HIP is not available | NHA's list for the module |
| `ABDM-1061` | | Consent artefact expired | NHA's list for the module |
| `ABDM-1062` | | Consent Not granted | NHA's list for the module |
| `ABDM-1063` | | Date Range given is invalid | NHA's list for the module |
| `ABDM-1064` | | request with this request id already exists | NHA's list for the module |
| `ABDM-1109` | | ABHA DB service unavailable | NHA's list for the module |
| `ABDM-1108` | | Notification DB service unavailable | NHA's list for the module |
| `ABDM-1201` | | IDP Gateway is unavailable | NHA's list for the module |
| `ABDM-9999` | | Unknown exception | NHA's list for the module |
| `ABDM-1006` | | Invalid combinations of scopes | NHA's list for the module |
| `ABDM-1056` | | This care contexts has been already linked | NHA's list for the module |
| `ABDM-1055` | | Invalid HIP Id or PHR Id | NHA's list for the module |
| `ABDM-1056` | | Invalid Link Reference Number | NHA's list for the module |
| `ABDM-1057` | | Invalid Care Contexts | NHA's list for the module |
| `ABDM-1059` | | Invalid Care Contexts count | NHA's list for the module |
| `ABDM-1060` | | Invalid Patient Reference Number | NHA's list for the module |
| `ABDM-1061` | | Invalid Patient Display | NHA's list for the module |
| `ABDM-1062` | | ABHA number mismatch with Link token | NHA's list for the module |
| `ABDM-1063` | | HIP Id mismatch with Link token | NHA's list for the module |
| `ABDM-1064` | | Request body was missing | NHA's list for the module |
| `ABDM-1065` | | Invalid X Auth token | NHA's list for the module |
| `ABDM-1066` | | Invalid JWT token | NHA's list for the module |
| `ABDM-1067` | | Request body not required | NHA's list for the module |
| `ABDM-1170` | | Invalid ABHA address | NHA's list for the module |
| `ABDM-1084` | | ABHA address mismatch with X Auth token | NHA's list for the module |
| `ABDM-1085` | | ABHA number mismatch with X Auth token | NHA's list for the module |
| `ABDM-1086` | | Patient profile mismatch with X Auth token | NHA's list for the module |
| `ABDM-1087` | | Duplicate patient share request | NHA's list for the module |
| `ABDM-1090` | | Duplicate HIP link request | NHA's list for the module |
| `ABDM-1091` | | Duplicate Get links request | NHA's list for the module |
| `ABDM-1092` | | Duplicate Link token request | NHA's list for the module |
| `ABDM-1093` | | Duplicate Bridge request | NHA's list for the module |
| `ABDM-1094` | | Duplicate bridge patch request | NHA's list for the module |
| `ABDM-1095` | | Duplicate Bridge service request | NHA's list for the module |
| `ABDM-1102` | | Profile information cannot be null | NHA's list for the module |
| `ABDM-1103` | | Duplicate Discovery request | NHA's list for the module |
| `ABDM-1104` | | Duplicate Init request | NHA's list for the module |
| `ABDM-1105` | | Duplicate Confirm request | NHA's list for the module |
| `ABDM-1106` | | Duplicate On discovery request | NHA's list for the module |
| `ABDM-1107` | | Duplicate On init request | NHA's list for the module |
| `ABDM-1108` | | Duplicate On confirm request | NHA's list for the module |
| `ABDM-1109` | | Invalid On discovery response | NHA's list for the module |
| `ABDM-1110` | | Invalid On init response | NHA's list for the module |
| `ABDM-1111` | | Invalid On confirm response | NHA's list for the module |
| `ABDM-1112` | | Invalid or already expired consent artefact id | NHA's list for the module |
| `ABDM-1113` | | Duplicate health information provider data flow response | NHA's list for the module |
| `ABDM-1149` | | Intent type is not supported at HIP end | NHA's list for the module |
| `ABDM-1150` | | Bridge API version cannot be null | NHA's list for the module |
| `ABDM-1402` | | Acknowledgement is not received from HIP | NHA's list for the module |
| `ABDM-9999` | | HIP is unable to generate a token at this time. Please try again later. | NHA's list for the module |
| `ABDM-9999` | | HIP is unable to process at this time. Please try again later. | NHA's list for the module |
| `ABDM-1030` | | Request id not found | NHA's list for the module |
| `ABDM-2500` | | No mapping found for | NHA's list for the module |
| `ABDM-2401` | | The X Auth token is invalid. | NHA's list for the module |
| `ABDM-2402` | | Invalid Timestamp | NHA's list for the module |
| `ABDM-2404` | | Invalid Request Id | NHA's list for the module |
| `ABDM-2429` | | Too many requests found | NHA's list for the module |
| `ABDM-2501` | | Payment status should be : SUCCESS,CANCELED,PENDING,FAIL,REFUND_INITIATED,REFUND_SUCCESS | NHA's list for the module |
| `ABDM-2500` | | Authorization header is missing | NHA's list for the module |
| `ABDM-2403` | | Invalid X-CM-ID | NHA's list for the module |
| `ABDM-2406` | | Invalid API sequence flow, please follow logical flow | NHA's list for the module |
| `ABDM-2406` | | The status is invalid. Please follow the logical status flow or transition. | NHA's list for the module |
| `ABDM-9999` | | User not found | NHA's list for the module |
| `ABDM-9999` | | Cannot process the request at the moment, please try later. | NHA's list for the module |
| `ABDM-9001` | | No open order against ABHA. Please ensure a minimum of one open order | NHA's list for the module |
| `ABDM-9002` | | No registration found at <<hospital name>>. Contact counter support | NHA's list for the module |
| `ABDM-9003` | | Hospital services temporarily unavailable. Please try again after some time. | NHA's list for the module |
| `ABDM-9004` | | Services disrupted, please try again. | NHA's list for the module |
| `ABDM-9005` | | Bank server not responding. Please try again later | NHA's list for the module |
| `ABDM-9006` | | Service details mismatch. Please ensure original service ID from HMIS | NHA's list for the module |
| `ABDM-9007` | | The Scan and Pay functionality is not enabled at this facility. Kindly contact the hospital administration. | NHA's list for the module |
| `ABDM-1006` | | Invalid count, must be 2 digit and ranges between 1 to 20 | NHA's list for the module |
| `ABDM-1407` | | The ABHA Number associated with this ABHA Address is currently deactivated. Please reactivate it. | NHA's list for the module |
| `ABDM-1006` | | Invalid HIType, it must be in Prescription,DiagnosticReport,OPConsultation,DischargeSummary,ImmunizationRecor… | NHA's list for the module |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,639 @@
# Design M2, create and link records
What the integration has to do to the journey around the calls: how many questions a patient is asked, where a failure is shown, and what a screen is forbidden to claim. Every rule below comes from a Catalogue atom, cited at the end.
## The unit of work is an exchange, not a call
### In plain words
Every M2 and M3 call answers twice. Once straight away, saying the request was
accepted, and later on a callback to your own bridge, saying what actually
happened. Do not treat the first as success. It says the request was accepted,
not that anything was linked.
So the request id stops being a log handle and becomes the join key. An
outbound call, the wait, and the callback that answers it are one exchange, and
they should read as one row with a timeline. Shown as three separate lines they
read as noise, and the difference between accepted and done stays invisible,
which is the one thing the interface exists to show.
### What happens
Register the waiter before sending. A callback that arrives before the HTTP
response returns is a race the sandbox eventually wins.
Then hold five outcomes apart. These must not look alike:
| State | Means | Observed |
|---|---|---|
| sent | in the air | |
| accepted, waiting | `202`, nothing back yet | real patient, 340 ms |
| answered | the callback arrived | |
| refused before waiting | a synchronous `400`, nothing was ever pending | fictional patient, 91 ms |
| no answer in the window | accepted, then silence | real patient, 60,000 ms |
The fourth and fifth are the ones implementations collapse into each other. A
refused call closing its waiter through the same function as an answered one
renders a synchronous `400` as a green callback arrived. Fix that in the shared
function rather than at the call sites that reach it.
The fourth outcome is also useful. Generate link token validates the patient
before accepting the request, so an identity nobody holds is refused at once
with `400` and `ABDM-9999`, while a recognised one is accepted with `202` and
answers later. The two are distinguishable at the first response, so only a
recognised patient is worth waiting for. Note the code: `ABDM-9999` is the catch
all, and the message is the only part naming the cause.
No timeout is published for any of these, so the honest words for the fifth
outcome are that nothing arrived in the window you chose.
### How you know it worked
Send one linking call and read one row. It carries the request id, the outbound
call, the elapsed wait, and the callback when it lands, in one timeline.
Send a call for an identity nobody holds. The row shows refused within a few
hundred milliseconds, never shows waiting, and no waiter is left open.
### When it goes wrong
- A synchronous refusal shows as a completed exchange. The refusal path and the
callback path share the function that closes the waiter.
- A callback cannot be matched to its call. The waiter was registered after
sending, and the callback won the race.
- Every call is accepted and nothing ever answers. No callback URL is registered
against the facility id, which is a configuration cause for a runtime symptom.
## Nobody is standing there, so never block and never lose the state
### In plain words
M1's rules do not carry over to M2. There is no patient to spare a question, no
one time password to save, and no screen anybody is looking at. The patient has
left. What there is instead is a call that was accepted and then went quiet, and
a callback that may arrive tomorrow or never.
Two consequences follow, and they are the opposite of how an M1 journey is
built.
### What happens
**Never block.** Linking is several calls, each answered on a callback that may
be a minute away. A button that waits for the outcome is a button that hangs for
sixty seconds and then says nothing useful. Start the work, return at once, and
point at where it can be watched. The desk gets on with the next patient.
**State outlives the session.** Where a link has got to belongs on the record,
not in a page's memory. Reopening the application must show where everything
stands without re-running anything. This is the reverse of M1, where the whole
journey lives and dies inside one person's visit.
**Name the step, not the spinner.** Linking tells a receptionist nothing. These
do:
- Asking ABDM for a link token.
- Asking ABDM for a link token: ABDM accepted it and no answer came back. This
client has no callback URL registered, so the answer has nowhere to go.
- Asking ABDM for a link token: refused. `ABDM-9999`, user not found.
The second is the interesting one. It names a configuration cause for a runtime
symptom, which is the difference between a receptionist raising a ticket and an
integrator fixing one line of configuration.
### How you know it worked
Start a link and close the page. Reopen the application and the record shows
which step the link reached, and when, without any call being made again.
Press the button that starts linking. It returns immediately and points at where
progress can be watched. It does not sit waiting for a callback.
### When it goes wrong
- The interface hangs for sixty seconds and then reports nothing. Something is
waiting on the callback inside the request that started the work.
- Reopening the application shows a link as not started when it was started
yesterday. The state was held in the page rather than on the record.
- A receptionist raises a ticket for what turns out to be a missing callback
URL. The message named the symptom without naming the configuration cause.
## A refused request is still remembered, so a retry is a duplicate
### In plain words
ABDM records a link token request even when it refuses the patient, and
deduplicates the next identical request against the one it refused. So a retry
inside that window cannot succeed, and its refusal no longer names the original
cause.
The second message says duplicate, which reads as though the first attempt had
worked. It had not.
### What happens
The same request for the same unknown address, sent twice:
| When | Result |
|---|---|
| 14:17:42 | `400`, `ABDM-9999`, user not found |
| 14:18:51, 69 seconds later | `400`, `ABDM-1092`, duplicate link token request |
The length of the deduplication window is not published. It is at least sixty
nine seconds.
So do not offer a button that cannot work. Where a duplicate is the answer,
explain it, and say that the earlier refusal is the one that names the cause.
Point the reader back at the first exchange rather than at the second.
This is the reason the first refusal has to be kept rather than replaced. An
interface that overwrites the last error with the newest one destroys the only
message that explained anything.
### How you know it worked
Send a request that is refused, then send it again inside the window. The
interface shows `ABDM-1092`, does not present it as a new failure, and links
back to the first exchange and its `ABDM-9999`.
The retry control is absent or disabled while the window is open, rather than
present and failing.
### When it goes wrong
- A retry loop runs and every attempt after the first says duplicate. Nothing is
checking whether a retry can succeed.
- The cause of a failure cannot be found because the newest message replaced it.
Keep every exchange, and treat the first refusal as the authoritative one.
- A duplicate refusal is read as evidence that the first request succeeded.
It is not. The first was refused, and ABDM remembered the refusal.
## What the integrator needs on screen, separately from the patient
### In plain words
A desk that talks to ABDM asynchronously cannot be debugged without a view built
for the integrator rather than the receptionist. It is cheap to build and it is
the difference between a fix that takes a minute and one that takes a day.
### What happens
**A live view of every call and callback, grouped by request id**, so an
outbound call, its wait, and the callback answering it read as one exchange with
a timeline.
**Redact by name and length, never by value.** Show that an `Authorization`
header was sent and how long it was. Never its contents. The panel is a browser.
**Show what would be sent, before sending it.** A request preview or a bundle
inspector turns it failed at the far end into something checkable at the desk.
**A readiness check that names what is missing**, and this is where two
configuration failures have to be told apart:
| Missing | Effect | Treat as |
|---|---|---|
| Facility id | the call cannot be sent | blocker, refuse locally |
| Callback URL | the call sends and is accepted, and the answer has nowhere to go | warning, send anyway |
Treating both as blockers is the tempting mistake. Refusing to send when no
callback URL is registered hides exactly the behaviour the panel exists to make
visible. Send, accept, wait, time out, and say why.
Refuse the first locally and name the value. An invented facility id comes back
from ABDM as an entitlement error that reads like a credentials problem, and
sends the integrator to the console instead of to one line of configuration.
Presence is not validity. `X-HIU-ID` is checked for presence rather than
against any registry: absent is refused `401`, and an invented id is accepted
`202`. Anyone testing with a made up id sees acknowledgements that look like
progress, which is why the local check has to be the strict one.
### How you know it worked
Clear the facility id and press the button. The call is refused before anything
is sent, and the message names the configuration value that is absent.
Clear the callback URL and press the same button. The call is sent, accepted,
waits the window, and reports that nothing arrived and why.
Open the panel during a linking run. Each exchange is one row, headers are shown
by name and length, and no header value appears anywhere.
### When it goes wrong
- A token appears in the panel. Redaction is by value rather than by name and
length. This is a credential leak, not a display bug.
- An integrator is sent to the developer console by an entitlement error. The
facility id was not checked locally before the call went out.
- Calls are accepted and nothing ever answers, and the panel says only waiting.
Name the missing callback URL as the cause, rather than leaving the reader
with a timeout.
## Inbound is a surface, and silence there is a failure state
### In plain words
M2 and M3 are not only things your desk asks ABDM. ABDM asks your desk things
and waits: a discovery request, a link initiation, a link confirmation, a
request for the records a consent covers, and a consent notification.
Four of those five carry no documented payload in the published sources. A
handler written against an assumed shape fails on the first real delivery,
asynchronously, where nobody is watching. And a handler that only logs leaves a
patient's records undiscoverable while appearing to work.
### What happens
Answer the transport quickly and reason afterwards. ABDM is waiting on a `2xx`,
and a handler that thinks before it replies is a handler that times out.
Then give the interface a state for ABDM asked and this desk has not replied.
Not an empty list, and not silence. A named, visible condition, saying what a
correct reply would have been.
Where the payload is undocumented, record the entire body and the entire header
set on the first delivery of each path, before anything tries to read it.
Record header names and lengths, never values. The name is the finding. The
value is a credential.
Every inbound callback carries a bearer token in the `Authorization` header, as
`Bearer <token>`. Verify it before your handler does any work. See
[proving a callback came from ABDM](/docs/hiecm/v3/concepts/callback-authenticity).
The security rule underneath: a URL reachable by ABDM is reachable by everyone.
A presented signature that fails verification is refused everywhere. An absent
one may be tolerated on a sandbox, and where it is, the record has to be marked
as unverified rather than passed off as genuine.
### How you know it worked
Trigger a discovery against your bridge. The transport is acknowledged inside
the window, and the interface shows the delivery, the reply, and the time
between them.
Take a path you have never received before. The whole body and the whole header
name set are recorded before any code reads a field, so the shape can be learned
from the record rather than guessed.
Leave an inbound request unanswered on purpose. The interface names it as
unanswered rather than showing nothing.
### When it goes wrong
- A patient's records cannot be found from another facility, and nothing looks
broken. A discovery handler is logging and not replying.
- Deliveries time out under load. Work is being done before the acknowledgement
rather than after it.
- A handler throws on the first real delivery. It was written against an assumed
payload, and the body was never recorded whole.
## A care context and its records are one thing
### In plain words
A care context is the visit. The records are what happened at it. Neither is
useful alone.
A bundle nobody can name a care context for cannot be sent. A care context with
nothing behind it is a promise the next discovery cannot keep: another facility
finds the visit, asks for the records, and there are none.
So store them together and show them together.
### What happens
Show the counts that matter to a patient rather than the ones that are easy to
compute: visits opened, documents attached, and visits not yet findable
elsewhere. The third is the one worth surfacing, because it means a record
exists that no other facility can reach.
Validate before storing, not before sending. Refusing to store a bundle the
receiver could not read is cheap. Discovering it after a consent has been served
is not.
Let the desk inspect a bundle before it goes: the profile it claims, the
resources inside it, the attachment type, and whether every reference resolves
within the bundle. The upload itself wants a drop target, a title prefilled from
the filename but editable, and named refusals for type and for size. Show a hash
and a size so a file is identifiable in a list without keeping a second copy of
it.
Write the title for the patient. They read it in their own application months
later, not the receptionist filing it today.
Never log the attachment. It is a patient's record, and it belongs in the bundle
and in the encrypted payload built from it, nowhere else.
### How you know it worked
Open a visit and attach nothing. The interface counts it as a visit not yet
findable elsewhere, rather than as a completed link.
Attach a document and the same count falls by one.
Try to store a bundle whose references do not resolve inside it. Storing is
refused, and the refusal names the reference that pointed outward.
### When it goes wrong
- Another facility discovers a visit and receives nothing. A care context was
linked with no records behind it.
- A bundle is refused at the far end, asynchronously, with no explanation
reaching the desk. It was validated before sending rather than before storing.
- A list of visits becomes slow to open. Bundles are being held in the list
rather than stored separately and read when needed.
## Asynchronous calls and callbacks, why a 200 means very little
### In plain words
Nothing goes participant to participant. Every request is addressed to the gateway, which forwards it. Two things follow.
- **You get an acknowledgement, not an answer.** In the [M3](/docs/hiecm/v3/getting-started/glossary#m3) consent flow the [HIU](/docs/hiecm/v3/getting-started/glossary#hiu) asks, the HIE-CM returns the consent request id on a callback, and the patient's decision comes back later. Each call's page in the [API reference](/docs/hiecm/v3/api) names the callback it produces.
- **You have to be reachable.** Half of [M2](/docs/hiecm/v3/getting-started/glossary#m2) is endpoints the gateway calls on your system.
One exception. In the health information flow the HIU supplies a data push URL, and the HIP encrypts the records and pushes them there. That URL may differ from the HIU's registered gateway URL, to improve privacy. The permission came through the gateway. The bytes do not.
Match each callback to the call that caused it by `response.requestId`, which carries the `REQUEST-ID` you sent. Callbacks do not arrive in the order you sent the requests, and the same one can arrive twice, so a repeat must change nothing. Each callback is described in [the API reference](/docs/hiecm/v3/api).
### What happens
Send the call with a fresh `REQUEST-ID`, treat the 202 as receipt only, and wait for the callback. Key the handler on `response.requestId`, answer it quickly, and make a second delivery of the same request id a no-op.
### How you know it worked
A callback reaches your URL whose `response.requestId` equals the `REQUEST-ID` you sent. Given three calls and two callbacks, the call whose request id has no callback is the one outstanding.
### When it goes wrong
Nothing arrives: work through [the callback never arrives](/docs/hiecm/v3/troubleshooting/callback-never-arrives). A retry is appended as a new event: key on the request id. The code waits on the response body for the result: it never comes there, so the integration hangs.
## Proving a callback really came from ABDM
### In plain words
Every callback in the specifications declares bearer authentication. The token
arrives in the `Authorization` header as `Bearer <token>`. Check three things
before your handler does any work:
1. **A bearer token is present.** Reject a callback without one, and log the
rejection.
2. **An answer answers a call you made.** Its `response.requestId` matches the
`REQUEST-ID` of a request you sent. A callback that answers nothing you sent
is not yours to act on.
3. **It is for a facility you serve.** `X-HIP-ID` or `X-HIU-ID` names one of
your own facilities. This is the only check that covers a callback that
answers nothing, such as a discovery request, a link init or a consent
notification. A bridge can receive callbacks for services that are not
yours: on a shared sandbox bridge, another organisation's consent
notifications and link callbacks have arrived at an integrator's URL.
Storing one keeps another organisation's patient data, and answering one
acts on its behalf.
The keys that verify the token's signature are not among the published gateway
calls. Confirm at onboarding how to verify the token, and meanwhile hold the
three checks above.
The signature inside a consent artefact is a different thing. It signs the
artefact's contents and proves the artefact was not altered. Checking the
delivery does not check the artefact, and checking the artefact does not check
the delivery.
### How you know it worked
A callback carrying a bearer token and a request id you sent is processed. The
same body with the `Authorization` header removed is rejected before your
handler reads the payload, and the rejection is logged. The second is the test
worth writing, because it is the only one that fails loudly when the check is
silently skipped.
### When it goes wrong
**The check is skipped under load.** A handler that checks inside a try block
and continues on failure is worse than one that never checked, because it reads
as safe. Fail closed.
**Nothing arrives at all**, which is a different problem. See
[the callback never arrives](/docs/hiecm/v3/troubleshooting/callback-never-arrives).
### What happens
In the handler, before parsing the body for action: require the `Authorization` header with a bearer token; require that `X-HIP-ID` or `X-HIU-ID` names one of your facilities; and, on a callback that answers a call, require that `response.requestId` matches a `REQUEST-ID` your system sent and has not already handled. Reject otherwise. Do not invent a signature check against a key source the specifications do not publish.
### How you know it worked
A test posts a valid callback body without `Authorization` and sees it rejected and logged before any handler work.
### When it goes wrong
Never fall back to processing a callback that failed a check while you investigate. A callback whose request id is unknown to you, or whose facility header names a facility you do not serve, is logged and dropped, not stored, answered or retried.
## Care contexts, how records are grouped so they can be found
### In plain words
Each care context contains only two pieces of information:
- **Reference ID.** A unique internal identifier assigned by the
[HRP](/docs/hiecm/v3/getting-started/glossary#hrp) (HMIS/LMIS). Used to link
and retrieve the associated health records.
- **Display Name.** A user-friendly description to help identify the group of
records. Must not include any sensitive or confidential information such as
test results or diagnoses. Example: "OPD records (X-Ray, Prescription) from
3rd March 2023".
### Recommended approach for structuring care contexts
To ensure clarity and usability, it is recommended to organise patient data as
follows:
- Create one care context per outpatient visit (OPD)
- Create one care context per inpatient admission (IPD)
This approach provides a clear and event-based grouping of health records.
### JSON structure of care contexts
```json
{
"patient": {
"referenceNumber": "TMH-PUID-001",
"display": "TMH records for Kiran Kumar",
"careContexts": [
{
"referenceNumber": "2375639",
"display": "OPD records for 03 Oct 2022"
}
]
}
}
```
### What happens
Give each care context a `referenceNumber` your own system resolves to the records behind it, and a `display` a person recognises months later. Group by encounter: one per outpatient visit, one per inpatient admission, not one per test or document.
### How you know it worked
Three tests in one visit are one care context. A display name reads like "OPD records for 03 Oct 2022" and carries no diagnosis or result.
### When it goes wrong
A diagnosis or result in `display` leaks clinical information into a system built never to hold it, visible to anyone who can list the patient's care contexts. One care context per record produces a list no person can navigate.
## How a health record is encrypted between HIP and HIU
### In plain words
The scheme is [Elliptic Curve Diffie-Hellman](/docs/hiecm/v3/getting-started/glossary#ecdh) key exchange on Curve25519, with AES-GCM for the payload and HKDF to derive the session key. Only the HIU holding valid consent can read the data, and the design gives perfect forward secrecy: key material compromised later does not expose data exchanged earlier.
### Who holds which key
| Key material | Generated by | Where it goes |
| --- | --- | --- |
| Short term private key, DHSK(U) | HIU | Never leaves the HIU |
| Short term public key, DHPK(U) | HIU | Sent with the request |
| Nonce, RAND(U), 32 bytes | HIU | Sent with the request |
| Short term private key, DHSK(P) | HIP | Never leaves the HIP |
| Short term public key, DHPK(P) | HIP | Sent with the encrypted data |
| Nonce, RAND(P), 32 bytes | HIP | Sent with the encrypted data |
| Shared key, DHK(U,P) | Computed independently by both | Never transmitted |
| Session key, SK(U,P), 256 bit AES-GCM | Derived independently by both | Never transmitted |
| Long term private key | HIP | Never leaves the HIP. Signs the encrypted payload. |
A new key pair per exchange is what buys forward secrecy.
### What the HIP does, step by step
Six steps, once consent has validated.
1. Generate a key pair, DHSK(P) and DHPK(P), in the group the HIU specified.
2. Generate a 32 byte random value, RAND(P).
3. Compute the shared key DHK(U,P) from the HIU's public key DHPK(U) and the HIP's own private key DHSK(P).
4. Derive the salt and IV by XOR of RAND(P) and RAND(U). The first 20 bytes are the salt for HKDF, the last 12 bytes the IV.
5. Compute a 256 bit AES-GCM session key SK(U,P) with HKDF-SHA256, from the x coordinate of the shared key and that salt. The HKDF `info` is empty.
6. Encrypt the data with AES-256-GCM, that key and that IV, with no additional authenticated data. Append the 16 byte authentication tag to the ciphertext and base64 encode the result.
The HIP then sends DHPK(P), RAND(P) and the encrypted data. The HIU derives the same session key from its own private key DHSK(U) and the HIP's public key DHPK(P), with salt and IV from the same XOR.
Build the shared key from the HIU's public key and the HIP's private key. That is the pairing that makes the Diffie-Hellman exchange work.
### Do not write this yourself
Two reference implementations exist. Fidelius, at [github.com/sukreet/fidelius](https://github.com/sukreet/fidelius), and the Fidelius CLI, which is Java, with worked examples for Node.js, Python, Ruby and PHP at [github.com/mgrmtech/fidelius-cli](https://github.com/mgrmtech/fidelius-cli/tree/main/examples) that run the binary as a subprocess. A webinar covers the CLI from both sides, at [youtu.be/rSir2gbkEmk](https://youtu.be/rSir2gbkEmk?t=9232) from 2:33:52.
### What happens
Generate a fresh key pair and nonce per transfer. XOR the two nonces: the first 20 bytes are the HKDF salt, the last 12 the IV. Derive the key with HKDF-SHA256 over the x coordinate of the ECDH result, with an empty `info`, for 32 bytes. Encrypt with AES-256-GCM, no additional authenticated data, and append the 16 byte tag before base64 encoding.
### How you know it worked
The HIU decrypts every entry and its tag check passes. Before any peer is involved, round trip a record through the Fidelius CLI and compare bytes.
### When it goes wrong
Every tag check fails: compare the salt and IV split, the HKDF input (the x coordinate, not the whole shared secret) and an `info` or additional data that should be empty. One HIP fails while others work: compare how its public key is encoded.
## Reading an ABDM error code
### In plain words
ABDM returns [several different error shapes](/docs/hiecm/v3/api/m1/errors), and
only some of them carry a code. Parse for all of them before you write any handling,
because the shape tells you where the failure came from.
Every code in the [error code reference](/docs/hiecm/v3/reference/error-codes)
carries an action. Key your handling to that column rather than to a list of
codes you maintain by hand.
| Action | What your code does |
| --- | --- |
| Fix request | Do not retry. Something you sent is wrong, and sending it again will not help |
| Fix auth | Fetch a fresh token, then retry once |
| New request id | Generate a new `REQUEST-ID`, then retry once |
| Retry | Back off and retry, with a ceiling on attempts |
| Cannot proceed | Stop, and tell the person why in their own terms |
| Ask support | Stop, and collect the ids before the context is lost |
| Unclassified | Treat 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](/docs/hiecm/v3/troubleshooting).
Read the code in the body before the HTTP status. A 404 can carry `ABDM-1016`,
Invalid Timestamp: the route exists and the request was refused. A 404 whose
body carries `Status report` and no code means no route matched the request. A
code can arrive bare or with a trailing colon and space, as in `ABDM-1016: `, so
match on the code itself.
### What happens
Parse the body for an error code before acting on the status. Strip a trailing colon and space from the code, then key the handling on the action column of the [error code reference](/docs/hiecm/v3/reference/error-codes), not on a list maintained by hand.
### How you know it worked
Given a code, you can say what it means and which header or field it concerns: `ABDM-2403` is Invalid X-CM-ID, the consent manager header.
### When it goes wrong
A one line message is read as a diagnosis: Invalid header covers many causes. A code that is not in the reference is handled as Unclassified, not guessed at. A 404 is treated as a wrong path when its body carries a code.
## Roles, which entity your software acts for and which way the record moves
### In plain words
There are two integrator roles on HIE-CM, and your product is one of them for its
whole life. What decides it is which entity your software acts for.
| Role | It acts for | What you build |
| --- | --- | --- |
| [IMS](/docs/hiecm/v3/getting-started/glossary#ims) | A care provider. An HMIS in a hospital, an EMR in a clinic, a LIMS in a laboratory, a PMS in a pharmacy | [M1](/docs/hiecm/v3/milestones/m1) to [M4](/docs/hiecm/v3/milestones/m4) |
| [PHR](/docs/hiecm/v3/getting-started/glossary#phr) | A care seeker, who holds their own records and gives consent | [P1](/docs/hiecm/v3/milestones/p1) to [P3](/docs/hiecm/v3/milestones/p3) |
[HIP](/docs/hiecm/v3/getting-started/glossary#hip) and
[HIU](/docs/hiecm/v3/getting-started/glossary#hiu) are not a third and a fourth
role, and they are not something you register as. They are the two ends of one
record moving: whoever publishes it is the HIP for that exchange, and whoever
asks to read one they did not create is the HIU.
Both roles are both, and it changes call by call:
- A hospital is the HIP when it shares a discharge summary, and the HIU when it pulls an earlier prescription, through the same IMS.
- A citizen is the HIP when they push a record from their PHR application, and the HIU when they fetch one.
Neither is a thing you can build once and be. See
[HIP and HIU](/docs/hiecm/v3/concepts/hip-hiu).
So the milestones you build follow the direction your records move, not the kind
of product you sell. A PHR app that lets a citizen push a record publishes as
the HIP, and builds the M2 linking and transfer calls as well. See
[where the citizen is the HIP](/docs/hiecm/v3/milestones/p2#where-the-citizen-is-the-hip).
Records never pass through the consent manager: it routes the request and holds
the consent, and the record goes from the system that holds it to the system
that asked.
### What happens
Decide the role once, IMS or PHR, from the entity. Then list every direction a record moves through the product: publishing a record is HIP behaviour, fetching one it did not create is HIU behaviour. Build the milestones for each direction the product uses.
### How you know it worked
For a hospital system that also pulls a patient's history, you can name the role, IMS, both directions, and the milestones: M1, M2 and M3. For a PHR app that uploads a scanned prescription, you can say the citizen is the HIP for that record.
### When it goes wrong
HIP, HIU, health repository and health locker are chosen as though they were one list of company types: two are directions, one is custody, one is a product. A PHR app is built for P1 alone and then cannot publish the first record a citizen pushes. A fetch is designed against the consent manager, which holds no records.
## Where these came from
- `hiecm.concept.m2-exchange-not-call`
- `hiecm.concept.m2-never-block-the-desk`
- `hiecm.concept.m2-retry-is-a-duplicate`
- `hiecm.concept.m2-integrator-call-panel`
- `hiecm.concept.m2-inbound-is-a-surface`
- `hiecm.concept.m2-care-context-and-records`
- `hiecm.concept.asynchronous-callbacks`
- `hiecm.concept.callback-authenticity`
- `hiecm.concept.care-context`
- `hiecm.concept.data-flow-encryption`
- `hiecm.concept.error-codes`
- `hiecm.concept.roles`

View File

@ -0,0 +1,70 @@
# Integrate M2, create and link records
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
24 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/api/hiecm/consent/v3/request/hip/on-notify` | Acknowledge the notification sent when a consent request is approved/revoked/ex… |
| `POST` | `/api/hiecm/data-flow/v3/health-information/hip/on-request` | Health information data request acknowledgement from HIP. |
| `POST` | `/api/hiecm/data-flow/v3/health-information/notify` | Notifications corresponding to events during data flow |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/hiecm/hip/v3/link/carecontext` | Perform HIP initiated linking. |
| `POST` | `/api/hiecm/hip/v3/link/context/notify` | Notify CM about any update on the already linked care context for a patient. |
| `POST` | `/api/hiecm/hip/v3/link/patient/links/sms/notify2` | Send SMS notification to patient that a care context is linked. |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/on-confirm` | Sharing the response of /api/hiecm/user-initiated-linking/v3/link/care-context/… |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/on-init` | As a result of the initialization, HIP has to generate a unique reference-numbe… |
| `POST` | `/api/hiecm/user-initiated-linking/v3/patient/care-context/on-discover` | HMIS/LIMS/HIP has to ensure that only unlinked records of the patient has to be… |
| `POST` | `/api/hiecm/v3/token/generate-token` | Generate link token to link the health records |
| `POST` | `/api/v3/consent/request/hip/notify` | This is a callback API to notify HIP when consent is APPROVED or REVOKED. |
| `POST` | `/api/v3/hip/health-information/request` | Health information data request to HIP. |
| `POST` | `/api/v3/hip/link/care-context/confirm` | Confirm the linking of care contexts for a patient. It allows healthcare inform… |
| `POST` | `/api/v3/hip/link/care-context/init` | Initiate the linking of care contexts for a patient. It allows healthcare infor… |
| `POST` | `/api/v3/hip/patient/care-context/discover` | Discover care contexts associated with a patient. It allows healthcare informat… |
| `POST` | `/api/v3/hip/token/on-generate-token` | This is a call back API of [/api/hiecm/v3/token/generate-token]. |
| `POST` | `/api/v3/link/on_carecontext` | Is a callback API that will be called by HIE-CM. The response will be received … |
| `POST` | `/api/v3/links/context/on-notify` | This API endpoint is a call back API for /api/hiecm/hip/v3/link/context/notify … |
| `POST` | `/api/v3/patients/sms/on-notify` | This API endpoint is a call back API for /api/hiecm/hip/v3/link/patient/links/s… |
| `POST` | `/health-information/transfer` | health information transfer API |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIP-ID` | Identifier of the health information provider to which the request was intended |
| `X-LINK-TOKEN` | JWT Authentication token which was issued by ABDM after successful validation of username and password |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/hip/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"consentId": "e3c74829-3f82-4f94-959e-e10f57bcd57b"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```

View File

@ -0,0 +1,562 @@
# HIE-CM m2 build
Scaffolds an ABDM m2 integration one journey at a time. It covers care contexts, HIP initiated linking, discovery, and pushing encrypted records to a requester.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Link token (`m2-abdm-link-token-hip`)
**Act: the calls in this journey, in order**
#### 1. Generate link token to link the health records (`m2_post_v3_token_generate_token`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/v3/token/generate-token \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"abhaNumber": 12345678901234,
"abhaAddress": "<ABHA_ADDRESS>",
"name": "first_name + middle_name + last_name",
"gender": "M",
"yearOfBirth": 9999
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/v3/token/generate-token` is answered by `POST /api/v3/hip/token/on-generate-token` on your bridge.
### HIP initiated linking (`m2-abdm-hip-initiated-linking-hip`)
**Act: the calls in this journey, in order**
#### 1. Perform HIP initiated linking (`m2_post_hip_v3_link_carecontext`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/hip/v3/link/carecontext \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'X-LINK-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"abhaNumber": 12345678901234,
"abhaAddress": "<ABHA_ADDRESS>",
"patient": [
{
"referenceNumber": "TMH-PUID-001",
"display": "String",
"careContexts": [
{
"referenceNumber": "TMH-PUID-001",
"display": "display 1"
}
],
"hiType": "DiagnosticReport",
"count": 1
}
]
}'
```
#### 2. Notify a change to a linked care context (`m2_post_hip_v3_link_context_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/hip/v3/link/context/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"patient": {
"id": "<ABHA_ADDRESS>"
},
"careContext": {
"patientReference": "<ABHA_ADDRESS>",
"careContextReference": "b009a970-8b04-4779-abd1-b50f113245bf"
},
"hiTypes": [
"DiagnosticReport"
],
"date": "2024-05-09T10:34:00.387Z",
"hip": {
"id": "ABDM_HIP"
}
}
}'
```
#### 3. Send SMS notification to patient that a care context is linked (`m2_post_hip_v3_link_patient_links_sms_notify2`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/hip/v3/link/patient/links/sms/notify2 \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"phoneNo": "986543***",
"hip": {
"id": "ABDM_HIP",
"name": "ABC Hospital"
}
}
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/hip/v3/link/carecontext` is answered by `POST /api/v3/link/on_carecontext` on your bridge.
- `POST /api/hiecm/hip/v3/link/context/notify` is answered by `POST /api/v3/links/context/on-notify` on your bridge.
- `POST /api/hiecm/hip/v3/link/patient/links/sms/notify2` is answered by `POST /api/v3/patients/sms/on-notify` on your bridge.
### User initiated linking (`m2-abdm-user-initiated-linking-hip`)
**Act: the calls in this journey, in order**
#### 1. Answer the care context discovery (`m2_post_user_initiated_linking_v3_patient_care_context_on_8c9340`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/user-initiated-linking/v3/patient/care-context/on-discover \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"transactionId": "f901b782-bfdf-4224-9f8d-da2cadc20c0d",
"patient": [
{
"referenceNumber": "<ABHA_ADDRESS>",
"display": "12345",
"careContexts": [
{
"referenceNumber": "abc123",
"display": "12345"
}
],
"hiType": "Prescription",
"count": 1
}
],
"matchedBy": [
"MR"
],
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
#### 2. Link care context on init (`m2_post_user_initiated_linking_v3_link_care_context_on_init`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/user-initiated-linking/v3/link/care-context/on-init \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"transactionId": "f901b782-bfdf-4224-9f8d-da2cadc20c0d",
"link": {
"referenceNumber": "d353b782-bfdf-4224-9f8d-da2cadc20c0d",
"authenticationType": "DIRECT",
"meta": {
"communicationMedium": "MOBILE",
"communicationHint": "OTP",
"communicationExpiry": "2024-05-01T05:22:34.123Z"
}
},
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
#### 3. Link care context on confirm (`m2_post_user_initiated_linking_v3_link_care_context_on_confirm`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/user-initiated-linking/v3/link/care-context/on-confirm \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"patient": [
{
"referenceNumber": "<ABHA_ADDRESS>",
"display": "12345",
"careContexts": [
{
"referenceNumber": "abc123",
"display": "12345"
}
],
"hiType": "Prescription",
"count": 1
}
],
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
**Exit condition (Observe until this is true)**
A 202 on this reply, sent with `response.requestId` set to the `REQUEST-ID` of the `POST /api/v3/hip/link/care-context/confirm` request it answers. A reply without that id answers nothing.
### Consent and data flow (`m2-consent-management-data-flow-hip`)
**Act: the calls in this journey, in order**
#### 1. Acknowledge the consent notification (`m2_post_consent_v3_request_hip_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/hip/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"consentId": "e3c74829-3f82-4f94-959e-e10f57bcd57b"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
#### 2. Submit the health information data request acknowledgement from HIP (`m2_post_data_flow_v3_health_information_hip_on_request`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/hip/on-request \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"hiRequest": {
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"sessionStatus": "ACKNOWLEDGED"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
#### 3. Submit the notifications corresponding to events during data flow (`m2_post_data_flow_v3_health_information_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"consentId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"doneAt": "2023-01-24T06:35:44.167Z",
"notifier": {
"type": "HIP",
"id": "IN2810014366"
},
"statusNotification": {
"sessionStatus": "TRANSFERRED",
"hipId": "IN2810014366",
"statusResponses": [
{
"careContextReference": "10004-20200001768-1",
"hiStatus": "DELIVERED",
"description": "Data sent successfully"
}
]
}
}
}'
```
**Exit condition (Observe until this is true)**
A 202 response. The specification gives no body for it, so read what comes back.
### Callbacks (`m2-callbacks`)
**Act: the calls in this journey, in order**
#### 1. Receive the HIP token on generate token (`m2_post_v3_hip_token_on_generate_token`)
Inbound to your bridge at `/api/v3/hip/token/on-generate-token`. Acknowledge it and continue.
#### 2. Link on carecontext (`m2_post_v3_link_on_carecontext`)
Inbound to your bridge at `/api/v3/link/on_carecontext`. Acknowledge it and continue.
#### 3. Receive the links context on notify (`m2_post_v3_links_context_on_notify`)
Inbound to your bridge at `/api/v3/links/context/on-notify`. Acknowledge it and continue.
#### 4. Receive the patients SMS on notify (`m2_post_v3_patients_sms_on_notify`)
Inbound to your bridge at `/api/v3/patients/sms/on-notify`. Acknowledge it and continue.
#### 5. Discover care contexts associated with a patient (`m2_post_v3_hip_patient_care_context_discover`)
Inbound to your bridge at `/api/v3/hip/patient/care-context/discover`. Acknowledge it and continue.
#### 6. Initiate the linking of care contexts for a patient (`m2_post_v3_hip_link_care_context_init`)
Inbound to your bridge at `/api/v3/hip/link/care-context/init`. Acknowledge it and continue.
#### 7. Confirm the linking of care contexts for a patient (`m2_post_v3_hip_link_care_context_confirm`)
Inbound to your bridge at `/api/v3/hip/link/care-context/confirm`. Acknowledge it and continue.
#### 8. Receive the consent decision (`m2_post_v3_consent_request_hip_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/v3/consent/request/hip/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-HIP-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"status": "GRANTED",
"consentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"consentDetail": {
"schemaVersion": "v3",
"consentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"createdAt": "2024-05-01T05:10:20.123Z",
"patient": {
"id": "<ABHA_ADDRESS>"
},
"careContexts": [
{
"patientReference": "batman@tmh",
"careContextReference": "Episode1"
}
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "www.abc.com"
},
"hip": {
"id": "cowin_hip_01",
"name": "Cowin",
"type": "HIP"
},
"hiu": {
"id": "cowin_hiu_01",
"name": "Cowin",
"type": "HIU"
},
"consentManager": {
"id": "abdm"
},
"requester": {
"name": "<ABHA_ADDRESS>",
"identifier": {
"value": "REG1",
"type": "MH1001",
"system": "https://www.sample.com"
}
},
"hiTypes": [
"Prescription"
],
"permission": {
"accessMode": "VIEW",
"dateRange": {
"from": "2021-09-28T12:30:08.573Z",
"to": "2021-09-28T12:30:08.573Z"
},
"dataEraseAt": "2021-09-28T12:30:08.573Z",
"frequency": {
"unit": "HOUR",
"value": 1,
"repeats": 0
}
}
},
"signature": "e8nY601CYDsC0FKoDjSp+7GeQ2s2R8oZncLCz5ce+pEuDOr5bZV0aaHjwJg4b9S9V+twjt4hbojx3fl7egrt8+0c+lfPTi5/bBUAQXCABTfFmtFU7jn65HlTt8kgkiONx26ZBhJ0wX3xjYI72PPtzYIiT5Q08YtDoILA62KceioV7lwuKssw7wC4ECbBAvRuXT121TmtrPhf+0myJATSnaajS06S6OthrKfZLNTUFf3pFiJzqouSTrjNblOX6DT2+JuO3rom1Szz/03c0HQG+wWASv+PO3J6uRs0UI4JvKmM/4tP+Z+/HPKM15K5U5K+4pqf6czKrbIDpkT/kP8bGg==",
"grantAcknowledgement": false
}
}'
```
#### 9. Receive the health information data request to HIP (`m2_post_v3_hip_health_information_request`)
Inbound to your bridge at `/api/v3/hip/health-information/request`. Acknowledge it and continue.
#### 10. Push the encrypted records to the HIU's data push URL (`m2_post_health_information_transfer`)
Outbound from your bridge. POST each page of encrypted records to the `dataPushUrl` in the health information request of step 9. The call goes straight to the HIU, not through the gateway. The HIU answers 202 for each page.
**Exit condition (Observe until this is true)**
A 202 response. The specification gives no body for it, so read what comes back.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/m2
- Error codes: /docs/hiecm/v3/api/m2/errors

View File

@ -0,0 +1,91 @@
---
name: abdm-m3
description: Use when building, debugging or testing ABDM Milestone 3: raising a consent request, tracking its status, reading consent artefacts, and fetching encrypted health records as an HIU. Carries the endpoints and the consent rules. Also carries the scaffolding loop that builds it flow by flow and the loop from a failed call to a named fix, in references/.
type: skill
domain: m3
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
- hiu-registration
- callback-url
- abha-address
produces:
- consent-request-id
- consent-artefact
can_execute: true
can_orchestrate: false
---
# ABDM M3, fetch data with consent
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m3/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M3
- Ask a patient, through their consent manager, for records another provider holds.
- Track that request through a grant, a denial, a revocation and an expiry.
- Receive the encrypted records at your data push URL and decrypt them.
- Handle one request that produces several consent artefacts.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Raise a consent request and fetch the records it covers"
- "What has to be in place before my first M3 call?"
- "My M3 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 16 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
- One consent request produces one artefact per HIP, so store the request id and every artefact id. Taking the first element is the bug that silently drops half a fetch.
- You act as the HIU. The HIE-CM holds the consent and asks the patient on your behalf. No artefact, no records.
- The patient must be known to you by ABHA address before you can raise a request.
- Records arrive encrypted at the `dataPushUrl` the health information request names. Decrypt them with the key material that request carries.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m3
- The flows as diagrams: /docs/hiecm/v3/milestones/m3
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug M3, fetch data with consent
The codes this module can return, with the message and the operation that returns each.
## Errors
The M3, fetch data with consent specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,156 @@
# Design M3, fetch data with consent
What the integration has to do to the journey around the calls: how many questions a patient is asked, where a failure is shown, and what a screen is forbidden to claim. Every rule below comes from a Catalogue atom, cited at the end.
## Read plural as plural, and never translate a code into a meaning
### In plain words
Two habits cause most of the avoidable failures on the consent side, and both
are about reading what came back rather than what you expected.
A consent request produces one artefact per HIP, so a request that spans
facilities returns several. Taking the first element is
the bug that silently drops half a fetch.
And the same error code means different things in different modules, so a code
alone is not a meaning.
### What happens
Hold the collection. A length of one is a case, not the normal case. It is worth
having a function whose existence prevents any call site from reaching for the
first element, because the failure is silent: half the records simply never
arrive, and nothing reports an error.
Carry ABDM's message through to the screen and never translate a code into words
of your own. `ABDM-1112` is an unusable consent in one place and a DigiLocker
gender mismatch in a module error table. `ABDM-1062` is a withheld consent in
one and a link token mismatch in another. Whichever you choose to display, you
will be wrong for the other module, and the message ABDM sent is the only part
that names the actual cause.
Handle two answers from consent status and consent fetch. The specification
documents them as synchronous responses carrying a status and an artefact
array. One run against ids that did not exist returned `202` with an empty
body instead. That single observation is not a rule: check the status before
you read the body, treat a `200` as the documented answer, treat a `202` with
no body as an acknowledgement whose answer arrives on the callback, and confirm
which you get on your own data before building on either.
### How you know it worked
Raise a consent covering care contexts at two HIPs. The number of artefacts you
hold matches the number ABDM returned, and a fetch runs for each one.
Trigger a refusal. The screen shows ABDM's own message text alongside the code,
and no wording of your own has replaced it.
### When it goes wrong
- A fetch returns fewer records than the consent covers, and nothing errors.
Something took the first artefact and discarded the rest.
- A displayed explanation contradicts what ABDM sent. A code was translated
locally instead of the message being carried through.
- A `202` with an empty body is read as a failure, or a `200` with a body is
parsed as if it were empty. The status decides which answer you hold.
## Refuse to guess an undocumented step, and say so before the work starts
### In plain words
M3's data transfer needs a key derivation and a symmetric cipher over the shared
secret. Neither is in the specification. The
[data flow page](/docs/hiecm/v3/concepts/data-flow) and the
Fidelius reference give the
scheme: HKDF over the shared secret, and AES-GCM for the payload. Build the step
from those. A desk that reads only the specification cannot complete it, and no
amount of care in the code around it changes that.
What matters is where the integrator finds out. A desk that discovers it cannot
decrypt at the moment records arrive has discovered it in the worst place
available: asynchronously, after a consent has been served, with a patient's
records in hand and nothing to do with them.
### What happens
Build every step from what is documented. Where a step is documented nowhere,
throw on it with a message naming exactly what is missing. Not a generic
failure. The name of the step, and what would have to be published for it to
work.
Then report it in the readiness check up front, rather than at the moment the
first encrypted bundle arrives.
The rule generalises past encryption. Where a capability cannot work, say so
before somebody depends on it, in the place they would act on it. A capability
that is going to fail should fail in the readiness check, where an integrator is
already looking for problems, not in a flow where they are looking for records.
Do not add a dependency to paper over a scheme nobody has written down. A guess
with a package name on it is still a guess, and it is harder to find later
because it looks like a decision somebody made deliberately.
### How you know it worked
Open the readiness check before running anything. It names any step that is
documented nowhere as unavailable, and says which part is missing.
Run the flow anyway. Where a step is missing, it fails at that step with a
message naming it, rather than at a later point with a decoding error.
### When it goes wrong
- Records arrive and cannot be read, and the failure reads as a corrupt payload.
The undocumented step failed quietly somewhere earlier.
- A library appeared in the dependency list to solve the cipher, chosen from a
sample rather than from the data flow page. Check it implements HKDF and
AES-GCM as that page gives them.
- The readiness check is green and the capability does not work. The check is
testing configuration rather than capability.
## Consent, what it authorises and how it ends
### In plain words
There are five states, in the two sections a PHR app shows: Requests holds Requested, Denied and Expired; Approved holds Granted and Revoked.
```mermaid
stateDiagram-v2
[*] --> Requested: HIU raises a consent request
Requested --> Granted: Patient approves
Requested --> Denied: Patient refuses
Requested --> Expired: Patient does not act in time
Granted --> Revoked: Patient withdraws access
Granted --> [*]: Validity period ends
```
| State | What it means | What your system does |
| --- | --- | --- |
| Requested | The patient has not acted yet. | Wait. Poll the request status if you need to show progress. |
| Granted | The patient approved, and set how long the access lasts. | Fetch the artefact ids, then request the data. |
| Denied | The patient refused. | Stop. There is no partial result and no retry that changes the answer. |
| Expired | The patient did not act inside the window the HIU set on the request. | Raise a new request if the clinical need is still there. |
| Revoked | The patient withdrew a consent they had already granted. | Stop fetching under that artefact from that moment. |
Two clocks run here. The **request window** is how long the patient has to answer, set by the HIU, and running out produces Expired. The **consent validity period** is how long access lasts once granted, set by the patient as they grant, with a defined expiry date and time. Neither is the **date range**, which says which records are in scope by when the care happened: a consent granted today can cover records from 2019.
Granted is not permanent. Revoked and Expired are ordinary destinations, not faults, and your system will meet both in production.
### What happens
Store the consent request id from the init callback, then every consent artefact id a grant returns. Before each fetch, check the artefact is still usable: the patient can revoke at any time, and the validity period ends on its own.
### How you know it worked
A granted request returns at least one artefact id, and a fetch under it is accepted while the artefact is within its validity period and not revoked.
### When it goes wrong
A fetch that worked last week fails today because the patient revoked or the validity period ended; that is the system working. `ABDM-1062` is consent not granted, and `ABDM-1112` is an artefact id that is invalid or already expired. Records kept past the consent window are a legal problem, not a technical one.
## Where these came from
- `hiecm.concept.m3-plural-artefacts-and-codes`
- `hiecm.concept.m3-refuse-to-guess`
- `hiecm.concept.consent-artefact`

View File

@ -0,0 +1,55 @@
# Integrate M3, fetch data with consent
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
16 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/api/hiecm/consent/v3/fetch` | Fetch the consent details |
| `POST` | `/api/hiecm/consent/v3/request/hiu/on-notify` | Acknowledge the notification sent when a consent request is approved/denied/rev… |
| `POST` | `/api/hiecm/consent/v3/request/init` | Initiate the consent request |
| `POST` | `/api/hiecm/consent/v3/request/status` | Get consent request status |
| `POST` | `/api/hiecm/data-flow/v3/health-information/notify` | Notifications corresponding to events during data flow |
| `POST` | `/api/hiecm/data-flow/v3/health-information/request` | Health information data request from HIU. |
| `GET` | `/api/hiecm/data-flow/v3/health-information/request/status/{transaction-id}` | Get the current status of the Health Information Request. |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/v3/hiu/consent/on-fetch` | This is a callback API called by CM to provide fetched consent artefact details… |
| `POST` | `/api/v3/hiu/consent/request/notify` | This is a callback API to notify HIU when consent is APPROVED, DENIED or REVOKE… |
| `POST` | `/api/v3/hiu/consent/request/on-init` | Callback API of consent request for patient HIU. |
| `POST` | `/api/v3/hiu/consent/request/on-status` | Callback API of consent status request. |
| `POST` | `/api/v3/hiu/health-information/on-request` | Health information data request acknowledgement to HIU. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/fetch \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"consentId": "5f7a535d-a3fd-416b-b069-c97d021fbacd"
}'
```

View File

@ -0,0 +1,377 @@
# HIE-CM m3 build
Scaffolds an ABDM m3 integration one journey at a time. It covers raising a consent request, tracking it, and fetching the records it covers as an HIU.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Consent and data flow (`m3-consent-management-data-flow-hiu`)
**Act: the calls in this journey, in order**
#### 1. Initiate the consent request (`m3_post_consent_v3_request_init`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/init \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"consent": {
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "www.abc.com"
},
"patient": {
"id": "<ABHA_ADDRESS>"
},
"hip": null,
"hiu": {
"id": "cowin_hiu_01"
},
"careContexts": [
{
"patientReference": "batman@tmh",
"careContextReference": "Episode1"
}
],
"requester": {
"name": "<ABHA_ADDRESS>",
"identifier": {
"value": "REG1",
"type": "MH1001",
"system": "https://www.sample.com"
}
},
"hiTypes": [
"Prescription"
],
"permission": {
"accessMode": "VIEW",
"dateRange": {
"from": "2021-09-28T12:30:08.573Z",
"to": "2021-09-28T12:30:08.573Z"
},
"dataEraseAt": "2021-09-28T12:30:08.573Z",
"frequency": {
"unit": "HOUR",
"value": 1,
"repeats": 0
}
}
}
}'
```
#### 2. Get consent request status (`m3_post_consent_v3_request_status`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/status \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"consentRequestId": "5f7a535d-a3fd-416b-b069-c97d021fbacd"
}'
```
#### 3. Acknowledge the consent notification (`m3_post_consent_v3_request_hiu_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/request/hiu/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": [
{
"status": "OK",
"consentId": "e3c74829-3f82-4f94-959e-e10f57bcd57b"
}
],
"error": {
"code": "ABDM-1001",
"message": "unable to connect database"
},
"response": {
"requestId": "6f0b4665-a915-4c92-aa36-65afb4a2cd71"
}
}'
```
#### 4. Fetch the consent details (`m3_post_consent_v3_fetch`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/consent/v3/fetch \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"consentId": "5f7a535d-a3fd-416b-b069-c97d021fbacd"
}'
```
#### 5. Submit the health information data request from HIU (`m3_post_data_flow_v3_health_information_request`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/request \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-HIU-ID: IN2810014366' \
--header 'Content-Type: application/json' \
--data '{
"hiRequest": {
"consent": {
"id": "18235d89-cb13-479d-ad71-7a57d5f669a8"
},
"dateRange": {
"from": "2022-10-06T15:10:00.587Z",
"to": "2022-11-06T15:10:00.587Z"
},
"dataPushUrl": "https://live.ndhm.gov.in/api-hiu/data/notification",
"keyMaterial": {
"cryptoAlg": "ECDH",
"curve": "curve25519",
"dhPublicKey": {
"expiry": "2022-12-28T13:18:20.742Z",
"parameters": "Ephemeral public key",
"keyValue": "BFN7KTdOT0jIAExG2A8Jg+01wMPWxptiGqwHRVvtiVEsUq2FR7P2UdqZxJyPJSeR6muai21iQhasNxnhh8I5M+g="
},
"nonce": "28236d89-cb13-479d-ad71-7a57d5f669a9"
}
}
}'
```
#### 6. Submit the notifications corresponding to events during data flow (`m3_post_data_flow_v3_health_information_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"notification": {
"consentId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"doneAt": "2023-01-24T06:35:44.167Z",
"notifier": {
"type": "HIU",
"id": "100005"
},
"statusNotification": {
"sessionStatus": "RECEIVED",
"hipId": "IN2810014366",
"statusResponses": [
{
"careContextReference": "10004-20200001768-1",
"hiStatus": "OK",
"description": "Data received successfully"
}
]
}
}
}'
```
#### 7. Get the current status of the health information request (`m3_get_data_flow_v3_health_information_request_status_tra_550104`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/data-flow/v3/health-information/request/status/{transaction-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/consent/v3/request/init` is answered by `POST /api/v3/hiu/consent/request/on-init` on your bridge.
- `POST /api/hiecm/consent/v3/request/status` is answered by `POST /api/v3/hiu/consent/request/on-status` on your bridge.
- `POST /api/hiecm/consent/v3/fetch` is answered by `POST /api/v3/hiu/consent/on-fetch` on your bridge.
- `POST /api/hiecm/data-flow/v3/health-information/request` is answered by `POST /api/v3/hiu/health-information/on-request` on your bridge.
Then `GET /api/hiecm/data-flow/v3/health-information/request/status/{transaction-id}` returns a 200 whose body matches:
```json
{
"transactionId": "18235d89-cb13-479d-ad71-7a57d5f669a8",
"status": "TRANSFERRED"
}
```
### Callbacks (`m3-callbacks`)
**Act: the calls in this journey, in order**
#### 1. Receive the consent request for patient HIU (`m3_post_v3_hiu_consent_request_on_init`)
Inbound to your bridge at `/api/v3/hiu/consent/request/on-init`. Acknowledge it and continue.
#### 2. Receive the consent status request (`m3_post_v3_hiu_consent_request_on_status`)
Inbound to your bridge at `/api/v3/hiu/consent/request/on-status`. Acknowledge it and continue.
#### 3. Notify HIU when consent is APPROVED, DENIED or REVOKED (`m3_post_v3_hiu_consent_request_notify`)
Inbound to your bridge at `/api/v3/hiu/consent/request/notify`. Acknowledge it and continue.
#### 4. Receive the provide fetched consent artefact details to HIU (`m3_post_v3_hiu_consent_on_fetch`)
Inbound to your bridge at `/api/v3/hiu/consent/on-fetch`. Acknowledge it and continue.
#### 5. Receive the health information data request acknowledgement to HIU (`m3_post_v3_hiu_health_information_on_request`)
Inbound to your bridge at `/api/v3/hiu/health-information/on-request`. Acknowledge it and continue.
**Exit condition (Observe until this is true)**
A 200 response. The specification gives no body for it, so read what comes back.
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/m3
- Error codes: /docs/hiecm/v3/api/m3/errors

View File

@ -0,0 +1,88 @@
---
name: abdm-m4
description: Use when building, debugging or testing ABDM Milestone 4, the NHPR: creating an HPID, registering a healthcare professional on the HPR, onboarding a facility to the HFR, and linking that facility to a bridge. Carries the operations, their hosts and headers.
type: skill
domain: m4
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
produces:
- hpid
- facility-id
- bridge-facility-link
can_execute: true
can_orchestrate: false
---
# ABDM M4, register facilities and professionals
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-m4/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with M4
- Create an HPID for a professional and register them in the HPR.
- Register a facility in the HFR and carry it through to submission.
- Link a facility to a bridge and set the type of each link.
- Fetch the council, course, college, university and geography codes these calls take instead of names.
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Onboard this facility to the HFR and link its HIP bridge"
- "What has to be in place before my first M4 call?"
- "My M4 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 91 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- Neither registry moves a health record. M4 establishes who the professional is and what the facility is, so every record flow has a verified provider behind it.
- The register professional call carries an `hprToken` in its payload, beside the practitioner.
- The facility calls are keyed to a `trackingId`, and the facility status values in the examples include `Draft` and `Submitted`. Read the status rather than assuming a facility is complete.
- Register professional takes codes, not names. Fetch council, course, college, university, state, district and language from the master data APIs first.
- `POST /v1/bridges/MutipleHRPAddUpdateServices` links a facility to a bridge. Each entry names a `bridgeId`, a `hipName`, a `type` such as `HIP`, and whether it is `active`.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/m4
- The flows as diagrams: /docs/hiecm/v3/milestones/m4
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug M4, register facilities and professionals
The codes this module can return, with the message and the operation that returns each.
## Errors
The M4, register facilities and professionals specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,133 @@
# Integrate M4, register facilities and professionals
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://apihspsbx.abdm.gov.in/v4/int` NHPR, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
91 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/aadhaar/generateLink` | Generate Aadhaar Link |
| `POST` | `/aadhaar/isAuthenticated` | Is Aadhaar Authenticated |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `POST` | `/api/v1/auth/authPassword` | Login Via Password |
| `GET` | `/api/v1/auth/cert` | Get Public Certificate |
| `POST` | `/api/v1/auth/confirmWithAadhaarOtp` | Verify Aadhaar OTP |
| `POST` | `/api/v1/auth/init` | Send Via Aadhaar OTP |
| `POST` | `/api/v2/auth/loginViaMobileSendOTP` | Send Verify OTP |
| `POST` | `/apis/v1/doctors/fetch-documents-list` | Fetch Documents |
| `POST` | `/apis/v1/doctors/fetch-professional-info` | Get Professional Info |
| `POST` | `/apis/v1/doctors/generate-mobile-otp` | Generate Mobile OTP 2 |
| `POST` | `/apis/v1/doctors/generate-verification-email` | Send Verification Email |
| `POST` | `/apis/v1/doctors/regenerate-mobile-otp` | Regenerate Mobile OTP |
| `POST` | `/apis/v1/doctors/register-professional-new` | Register Professional |
| `POST` | `/apis/v1/doctors/resent-verify-email` | Resend Verification Email |
| `POST` | `/apis/v1/doctors/update-professional-new` | Update Professional |
| `POST` | `/apis/v1/doctors/verify-email-otp` | Verify Email OTP |
| `POST` | `/apis/v1/doctors/verify-mobile-otp` | Verify Mobile OTP |
| `GET` | `/apis/v1/masters/affiliated-board` | Get All Affiliated Board |
| `GET` | `/apis/v1/masters/affiliated-board/{id}` | Get Affiliated Board By ID |
| `GET` | `/apis/v1/masters/affiliated-board/states/{id}` | Get Affiliated Board By State ID |
| `GET` | `/apis/v1/masters/colleges/{id}` | Get College By State |
| `GET` | `/apis/v1/masters/colleges/{stateId}/{medicineId}` | Get College By State And Medicine ID |
| `GET` | `/apis/v1/masters/countries` | Get All Countries |
| `GET` | `/apis/v1/masters/countries/{id}` | Get Countries By ID |
| `POST` | `/apis/v1/masters/courses` | List Courses |
| `GET` | `/apis/v1/masters/district` | Get All Districts |
| `GET` | `/apis/v1/masters/district/{id}` | Get Districts By State |
| `GET` | `/apis/v1/masters/languages` | List Government Health Programmes |
| `GET` | `/apis/v1/masters/languages/{id}` | Get Pi Languages By ID |
| `GET` | `/apis/v1/masters/medical-councils` | Get All Medical Council |
| `GET` | `/apis/v1/masters/medical-councils/name` | Get Medical Council By System Of Medicine Name |
| `GET` | `/apis/v1/masters/nurse-councils` | Get All Nurse Councils |
| `GET` | `/apis/v1/masters/states` | Get All States |
| `GET` | `/apis/v1/masters/states/{id}` | Get Status |
| `GET` | `/apis/v1/masters/sub-districts` | Get All Sub Districts |
| `GET` | `/apis/v1/masters/sub-districts/{id}` | Get All Sub Districts 1 |
| `GET` | `/apis/v1/masters/system-of-medicines` | Get All Medical System |
| `GET` | `/apis/v1/masters/universites` | List All University |
| `GET` | `/apis/v1/masters/universites/{id}` | Get University By College |
| `POST` | `/apis/v1/uploads/upload-document` | Upload Documents |
| `POST` | `/FacilityManagement/v1.5/facility/bygeoLocation/searchWithinRadiusWithFilter` | Get Facility Within Radius With Filter |
| `POST` | `/FacilityManagement/v1.5/facility/search` | Search Facility 1 |
| `GET` | `/getPsuDetailsByMinistry` | Get Psu Data |
| `GET` | `/hpid/get/categories` | Fetch HPID Categories |
| `GET` | `/hpid/get/subCategories` | Fetch HPID Sub Categories From Category |
| `POST` | `/password/change/byPassword` | Change Password |
| `POST` | `/password/recover/byAadhaar` | Recover Password Via Aadhaar |
| `POST` | `/password/recover/byMobile/sendMobileOTP` | Generate Mobile OTP 1 |
| `POST` | `/password/recover/byMobile/verifyMobileOTP` | Verify Mobile OTP 1 |
| `POST` | `/password/recover/confirmByAadhaar` | Recover Password Confirm By Aadhaar |
| `POST` | `/password/reset/password` | Reset Password And Session |
| `POST` | `/password/resetPassword` | Reset Password |
| `POST` | `/search/address/filter/deduplicate` | Get Filtered Address Post |
| `POST` | `/v1.0/facility/search-facilities` | Search Facility |
| `POST` | `/v1.5/facility/additional-information` | V15Facility Additional Information |
| `POST` | `/v1.5/facility/basic-information` | V15Basic Facility Information |
| `POST` | `/v1.5/facility/detailed-information` | V15Facility Detailed Information |
| `POST` | `/v1.5/facility/fetch-facility-Sub-type` | Get All Facility Sub Type By Facility Type |
| `POST` | `/v1.5/facility/fetch-facility-type` | Get All Facility Type By Ownership And Sys Of Med |
| `GET` | `/v1.5/facility/get-master-data` | Get Master Data |
| `GET` | `/v1.5/facility/get-master-types` | Get All Master Type |
| `POST` | `/v1.5/facility/get-owner-subtype` | Get All Sub Types By Owner Ship Type And Sub Type |
| `POST` | `/v1.5/facility/get-specialities` | Get All Specialities By System Of Medicine Code |
| `GET` | `/v1.5/facility/lgd/districts` | Get All District By State ID |
| `GET` | `/v1.5/facility/lgd/states` | Get All States By LGD |
| `GET` | `/v1.5/facility/lgd/subdistricts` | Get All Sub District By District Code |
| `POST` | `/v1.5/facility/sendOtpToContact` | Send OTP To Contact |
| `POST` | `/v1.5/facility/submit-facility` | V15Submit Facility Details |
| `POST` | `/v1.5/facility/validateOtp` | Validate OTP |
| `GET` | `/v1/account/getIdCard` | Get Account Png Card |
| `GET` | `/v1/account/information` | Get User Profile By JWT |
| `GET` | `/v1/account/user-details/{hprId}` | Get User Details |
| `POST` | `/v1/bridges/MutipleHRPAddUpdateServices` | Facility Add And Update |
| `POST` | `/v1/forgot/hprId/aadhaar` | Retrieval Health ID By Aadhaar |
| `POST` | `/v1/forgot/hprId/mobile` | Retrieval Health ID By Mobile |
| `POST` | `/v1/forgot/hprId/mobile/generateOtp` | Generate Mobile OTP |
| `POST` | `/v1/registration/aadhaar/checkHpIdAccountExist` | Account Exist |
| `POST` | `/v1/registration/aadhaar/generateMobileOTP` | Generate Mobile OTP |
| `POST` | `/v1/registration/aadhaar/hpid/suggestion` | Get Suggesstion |
| `POST` | `/v1/registration/aadhaar/verifyMobileOTP` | Verify Mobile OTP |
| `GET` | `/v1/search/existsByHprId/{hprId}` | Exists By HPR ID |
| `GET` | `/v1/search/searchByHprId/{hprId}` | Search User By HPR ID |
| `GET` | `/v1/search/searchByMobile/{mobile}` | Search User By Mobile No |
| `POST` | `/v2/registration/aadhaar/createHprIdWithPreVerified` | Create HPR ID V2 |
| `POST` | `/v2/registration/aadhaar/demographicAuthViaMobile` | Demographic Auth Via Mobile |
| `POST` | `/v2/registration/aadhaar/verifyOTP` | Verify OTP |
| `GET` | `/v4/auth/logout` | Logout |
## Headers
| Header | What it is |
| --- | --- |
| `Authorization` | |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `x-hprid-auth` | The HPR token of the signed-in professional, from the HPR login. |
| `x-hprid-auth-verifier` | The HPR token of the professional verifying the facility submission. |
| `X-Token` | The HPR token of the signed-in professional, from the HPR login. |
## A request, in full
```bash
curl --request POST \
--url https://apihspsbx.abdm.gov.in/v4/int/aadhaar/generateLink \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'Content-Type: application/json' \
--data '{
"scopes": [
"nhpr-register"
],
"source": "NHPR"
}'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
---
name: abdm-p1
description: Use when building, debugging or testing ABDM P1 in a PHR app: creating an ABHA address and logging in to it.
type: skill
domain: p1
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- gateway-session-token
produces:
- phr-login
- abha-profile
can_execute: true
can_orchestrate: false
---
# ABDM P1, PHR registration and login
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p1/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P1
- PHR certificate and session token
- Create ABHA number, Aadhaar OTP
- Create ABHA address, mobile number
- Create ABHA address, ABHA number with ABHA OTP
- Create ABHA address, ABHA number with Aadhaar OTP
- PHR login, mobile number
- PHR login, ABHA address with mobile OTP
- PHR login, ABHA number with Aadhaar OTP
- PHR login, ABHA number with ABHA OTP
- PHR login, ABHA address with password
- PHR login, Aadhaar OTP
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Register a new user in this PHR app and log them in"
- "What has to be in place before my first P1 call?"
- "My P1 call returned ABDM-1107. What is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 15 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 4 error codes from the specification's examples. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p1
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,48 @@
# HIE-CM p1 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### 900902
**Specification example:** HTTP 401, `Missing Credentials`, on `p1_get_v3_phr_app_enrollment_isexists`.
**Exit condition: the original call now succeeds.**
### ABDM-1107
**Specification example:** HTTP 400, `Invalid combinations of scopes`, on `p1_post_v3_phr_app_login_verify`.
**Exit condition: the original call now succeeds.**
### ABDM-1211
**Specification example:** HTTP 400, `User not found.`, on `p1_post_v3_phr_app_login_search`.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**Specification example:** HTTP 400, `Invalid LoginId`, on `p1_post_v3_phr_app_enrollment_request_otp`.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/p1
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `900902` | 401 | Missing Credentials | `p1_get_v3_phr_app_enrollment_isexists` |
| `ABDM-1107` | 400 | Invalid combinations of scopes | `p1_post_v3_phr_app_login_verify` |
| `ABDM-1211` | 400 | User not found. | `p1_post_v3_phr_app_login_search` |
| `ABDM-9999` | 400 | Invalid LoginId | `p1_post_v3_phr_app_enrollment_request_otp` |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,74 @@
# Integrate P1, PHR registration and login
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://abhasbx.abdm.gov.in` ABHA service, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
15 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `POST` | `/abha/api/v3/phr/app/enrollment/enrol` | 3 flows: Enrol ABHA Address |
| `GET` | `/abha/api/v3/phr/app/enrollment/isExists` | 3 flows: isExists API, isExists API Copy |
| `POST` | `/abha/api/v3/phr/app/enrollment/request/otp` | 3 flows: OTP Request - Mobile, OTP Request - ABHA OTP, OTP Request - AADHAR OTP |
| `POST` | `/abha/api/v3/phr/app/enrollment/suggestion` | 3 flows: Suggestion API |
| `POST` | `/abha/api/v3/phr/app/enrollment/verify` | 3 flows: OTP Verify - Mobile, OTP Verify - ABHA OTP, OTP Verify - AADHAR OTP |
| `GET` | `/abha/api/v3/phr/app/login/public/certificate` | PHR Certificate |
| `POST` | `/abha/api/v3/phr/app/login/request/otp` | 5 flows: OTP Request - Mobile, OTP Request - ABHAADDRES Mobile, OTP Request - A… |
| `POST` | `/abha/api/v3/phr/app/login/search` | Search Auth Methods - ABHAAddress |
| `POST` | `/abha/api/v3/phr/app/login/verify` | 6 flows: Login OTP Verify - Mobile, Login OTP Verify - ABHAADDRES Mobile, Login… |
| `POST` | `/abha/api/v3/phr/app/login/verify/user` | 5 flows: Verify User, Verify - User |
| `POST` | `/abha/api/v3/profile/account/request/emailVerificationLink` | Email Verification Link |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for each request. |
| `TIMESTAMP` | Request timestamp in UTC, ISO-8601 with Z. |
| `T-token` | The transaction token from the preceding login step, sent with a `Bearer ` prefix. It is valid only for that … |
| `X-token` | The user token from a login or enrolment response, sent with a `Bearer ` prefix. It acts for that ABHA holder. |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
## A request, in full
```bash
curl --request POST \
--url https://abhasbx.abdm.gov.in/abha/api/v3/phr/app/enrollment/enrol \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'Content-Type: application/json' \
--data '{
"txnId": "27d444b7-2a3d-46d8-bf67-e5590b6c46b6",
"phrDetails": {
"mobile": "<ENCRYPTED_MOBILE>",
"firstName": "John",
"middleName": "",
"lastName": "Doe",
"yearOfBirth": "<DOB>",
"dayOfBirth": "",
"monthOfBirth": "<DOB>",
"gender": "M",
"email": "",
"profilePhoto": "",
"address": "<ADDRESS>",
"stateName": "Maharashtra",
"stateCode": "27",
"districtName": "<ADDRESS>",
"districtCode": "123",
"pinCode": "<PINCODE>",
"abhaAddress": "<ABHA_ADDRESS>",
"password": "<ENCRYPTED_PASSWORD>"
}
}'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
---
name: abdm-p2
description: Use when building, debugging or testing ABDM P2 in a PHR app: the PHR profile, linking an ABHA number, switching profiles, and linking, sharing and consent for the patient.
type: skill
domain: p2
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- phr-login
produces:
- consent-decision
can_execute: true
can_orchestrate: false
---
# ABDM P2, Consents Management
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p2/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P2
- PHR profile, update mobile
- PHR profile, update password
- Link ABHA number, ABHA OTP
- Link ABHA number, Aadhaar OTP
- PHR profile, switch profile
- PHR profile, card, QR code and session
- Quick OPD Registration
- User initiated linking
- Consent manager, HIU and HIP
- Consent manager, fetch and manage consent requests
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Link this user's records to their ABHA address from the PHR app"
- "What has to be in place before my first P2 call?"
- "My P2 call returned ABDM-9999. What is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 39 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The loop from a failed call to a named fix, and 2 error codes from the specification's examples. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p2
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,34 @@
# HIE-CM p2 debug
Every error below is an OODA loop: observe the error code and last request id, orient against the matched code, decide the fix, act, and observe whether the original step now succeeds. Applying a fix is not the exit condition; the original step succeeding is.
Loop limit: 5 passes per error.
## Errors
### 900902
**Specification example:** HTTP 401, `Missing Credentials`, on `p2_get_v3_phr_app_login_profile`.
**Exit condition: the original call now succeeds.**
### ABDM-9999
**Specification example:** HTTP 400, `Invalid LoginId`, on `p2_post_v3_phr_app_login_profile_request_otp`.
**Exit condition: the original call now succeeds.**
## Where the detail is
- The operation that returns each code: /docs/hiecm/v3/api/p2
## Every code in the specification
### Codes
| Code | HTTP | Message | Returned by |
| --- | --- | --- | --- |
| `900902` | 401 | Missing Credentials | `p2_get_v3_phr_app_login_profile` |
| `ABDM-9999` | 400 | Invalid LoginId | `p2_post_v3_phr_app_login_profile_request_otp` |
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,79 @@
# Integrate P2, Consents Management
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://abhasbx.abdm.gov.in` PHR application service, sandbox
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
39 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/abha/api/v3/phr/app/login/profile` | Get Profile |
| `POST` | `/abha/api/v3/phr/app/login/profile/link` | 2 flows: Link Request |
| `GET` | `/abha/api/v3/phr/app/login/profile/phrCard` | Get PHR Card |
| `GET` | `/abha/api/v3/phr/app/login/profile/qrCode` | Get QR Code |
| `GET` | `/abha/api/v3/phr/app/login/profile/request/logout` | Logout |
| `POST` | `/abha/api/v3/phr/app/login/profile/request/otp` | 3 flows: Send OTP - Update Mobile, Send ABHA OTP - Link-DeLink, Send Aadhaar OT… |
| `GET` | `/abha/api/v3/phr/app/login/profile/request/token` | Refresh Token |
| `GET` | `/abha/api/v3/phr/app/login/profile/switch-profile` | Switch Profile |
| `POST` | `/abha/api/v3/phr/app/login/profile/updateProfile` | Update Profile |
| `POST` | `/abha/api/v3/phr/app/login/profile/verify` | 4 flows: Verify OTP - Update Mobile, Verify Password - Update Password, Verify … |
| `POST` | `/abha/api/v3/phr/app/login/profile/verify/switch-profile/user` | Verify User Switch Profile |
| `GET` | `/api/hiecm/consent/v3/artefact` | Fetch all the consent artefact details of a patient. |
| `GET` | `/api/hiecm/consent/v3/artefact/{consentId}` | Fetch the consent artefact details associated with the artefact-ID. |
| `GET` | `/api/hiecm/consent/v3/artefact/request/{consentRequestId}` | Fetch all the consent artefact details associated with a consent request REQUES… |
| `POST` | `/api/hiecm/consent/v3/auto/approve` | Setup an auto-approval policy for given HIU. |
| `POST` | `/api/hiecm/consent/v3/auto/approve/{consentId}/disable` | Disable the auto-approval policy. |
| `POST` | `/api/hiecm/consent/v3/auto/approve/{consentId}/enable` | Enable the auto-approval policy. |
| `GET` | `/api/hiecm/consent/v3/request` | Fetch all the consent request details of a patient. |
| `GET` | `/api/hiecm/consent/v3/request/{consentRequestId}` | Get the consent request details by REQUEST-ID. |
| `POST` | `/api/hiecm/consent/v3/request/{consentRequestId}/approve` | Approve the consent request raised by HIU from PHR/mobile application. |
| `POST` | `/api/hiecm/consent/v3/request/{consentRequestId}/deny` | Deny the consent request raised by HIU from PHR/mobile application. |
| `POST` | `/api/hiecm/consent/v3/revoke` | Revoke the granted consent from PHR/mobile application. |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `GET` | `/api/hiecm/gateway/v3/govt-programs` | Fetch the list of govt programmes. |
| `GET` | `/api/hiecm/gateway/v3/providers` | Fetch the list of providers filtered by name. |
| `GET` | `/api/hiecm/gateway/v3/providers/{hip-id}` | Fetch the record for provider details for requested provider ID. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `GET` | `/api/hiecm/hip/v3/link/patient/links` | This is the PHR APP API, this API will used to fetch all link care-context for … |
| `GET` | `/api/hiecm/patient-share/v3/profile/getTokenDetails` | OPD Token History |
| `POST` | `/api/hiecm/patient-share/v3/share` | OPD token generation |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/confirm` | Confirm his/her health records. |
| `POST` | `/api/hiecm/user-initiated-linking/v3/link/care-context/init` | Link his/her health records. |
| `POST` | `/api/hiecm/user-initiated-linking/v3/patient/care-context/discover` | Discover his/her health records. |
| `POST` | `/api/v3/hiu/patient/care-context/on-confirm` | Receive confirmation of the linked care contexts for a patient. It provides det… |
| `POST` | `/api/v3/hiu/patient/care-context/on-discover` | Receive the discovered care contexts of a patient. It provides detailed informa… |
| `POST` | `/api/v3/hiu/patient/care-context/on-init` | Receive the initial linking of care contexts for a patient. It provides detaile… |
| `POST` | `/api/v3/hiu/patient/on-share` | This API will be invoked to the HIU for sharing the response of HIECM's /api/hi… |
## Headers
| Header | What it is |
| --- | --- |
| `X-token` | The user token from a login or enrolment response, sent with a `Bearer ` prefix. It acts for that ABHA holder. |
| `REQUEST-ID` | Unique UUID for each request. |
| `TIMESTAMP` | Request timestamp in UTC, ISO-8601 with Z. |
| `X-AUTH-TOKEN` | The user token issued at login. Send it beside X-token. |
| `R-token` | The refresh token from a login response, sent with a `Bearer ` prefix, exchanged for a new user token. |
| `T-token` | The transaction token from the preceding login step, sent with a `Bearer ` prefix. It is valid only for that … |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://abhasbx.abdm.gov.in/abha/api/v3/phr/app/login/profile \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'X-token: Bearer <JWT TOKEN>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-AUTH-TOKEN: <X_AUTH_TOKEN>'
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
---
name: abdm-p3
description: Use when building, debugging or testing ABDM P3 in a PHR app: reading, approving, denying, enabling, disabling and updating the patient's subscriptions and subscription requests, and the subscription request and notifications on the health locker side.
type: skill
domain: p3
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- phr-login
produces:
- subscription
can_execute: true
can_orchestrate: false
---
# ABDM P3, PHR subscriptions
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p3/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P3
- Subscription request and notifications, HIU side
- Subscription approval and management, PHR side
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Let this user approve or deny a subscription request"
- "What has to be in place before my first P3 call?"
- "My P3 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Design.** What the journey around the calls has to do, and what a screen is forbidden to claim. [references/design.md](references/design.md)
- **Integrate.** 18 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
- The design section is different in kind. Its rules come from building a working front desk against the sandbox, and each atom it cites names what was observed and the date it was seen.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p3
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug P3, PHR subscriptions
The codes this module can return, with the message and the operation that returns each.
## Errors
The P3, PHR subscriptions specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

View File

@ -0,0 +1,71 @@
# Design P3, PHR subscriptions
What the integration has to do to the journey around the calls: how many questions a patient is asked, where a failure is shown, and what a screen is forbidden to claim. Every rule below comes from a Catalogue atom, cited at the end.
## The five things a personal health record application must let a person do with consent
### In plain words
Consent is granted by a person, and the PHR app is where they do it. There is
a floor of five capabilities, and an app missing one leaves a person able to
give access they cannot inspect, change or withdraw.
1. **See the request**, with the HIU asking, the purpose, the record types,
the date range of records, how long the consent would last, and its status.
2. **Change it before allowing it**, where the request permits: the access
duration, the record date range, the categories shared, and the validity
period. This is the one most often left out, and the one that turns a
consent screen into a negotiation rather than a demand.
3. **Allow or refuse it.** The consent flow has three outcomes, not two:
approve, reject and ignore. An ignored request expires on the requester's
window, and the interface has to show that state.
4. **See what is already allowed**, so the person can tell which
organisations hold access right now. A list of past decisions is not the
same thing.
5. **Take it back** at any time. Two things follow: the status updates at the
consent manager, and sharing under that consent stops immediately, not at
the end of the period.
### What happens
Build a screen for each of the five. Approving posts to `/api/hiecm/consent/v3/request/{consentRequestId}/approve`, denying to `/api/hiecm/consent/v3/request/{consentRequestId}/deny`, and revoking a granted consent to `/api/hiecm/consent/v3/revoke`. Show Expired as its own state, never as Denied, because the person refused nothing.
### How you know it worked
One request goes through the whole arc in the app: seen, its date range narrowed, allowed, found in the list of live consents, and revoked. A fetch attempted under it afterwards fails.
### When it goes wrong
A screen that lists requests but not live consents leaves the person unable to revoke what they cannot see. Revocation shown as expiry hides a decision the person made. Leaving out modification turns consent into a notice.
## Subscriptions, and why a personal health record application needs one
### In plain words
Ask the user for consent before you create a subscription. An approved
subscription notifies your app when a care context is linked or updated. Surface these as device
notifications.
You need screens to list subscriptions, approve them, deny them and edit them.
Editing covers health information types, purpose, categories and the time period.
A subscription tells you a record exists. It is not consent, and it gives nobody
the record: reading it still needs a consent request, which is why a
subscription usually runs beside an auto approval policy.
### What happens
Raise the subscription with `/api/hiecm/subscription-requests/v3/init`; the request id arrives on `/api/v3/hiu/hiecm/subscription-requests/on-init`. The person approves it with `/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/approve` or denies it. Once approved, notifications arrive on `/api/v3/hiu/subscription/notify`, in the categories `LINK` and `DATA`, and you acknowledge each with `/api/hiecm/subscription-requests/v3/hiu/care-context/on-notify`. Set one up when you create an address and when a person signs in with an address the install has not seen.
### How you know it worked
The subscription shows as approved, and linking a care context to that address from a facility produces a notification on your callback without any call from you.
### When it goes wrong
Nothing arrives: the checks are those for any callback, see [the callback never arrives](/docs/hiecm/v3/troubleshooting/callback-never-arrives). A subscription created without asking the person is a consent failure that certification looks for. A notification acted on as though it were permission reads a record with no consent behind it.
## Where these came from
- `hiecm.concept.consent-in-a-phr-app`
- `hiecm.concept.phr-subscriptions`

View File

@ -0,0 +1,53 @@
# Integrate P3, PHR subscriptions
The calls themselves: where they live, what they need in their headers, and one request written out in full.
## Hosts
- `https://dev.abdm.gov.in` ABDM gateway, sandbox
- `https://apis.abdm.gov.in` ABDM gateway, production
## Endpoints
18 operations, grouped by the journey they belong to.
### Other operations
| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id}` | Fetch the details of a service ID. |
| `GET` | `/api/hiecm/gateway/v3/bridge-services` | Fetch the service ids registered against a bridge. |
| `PATCH` | `/api/hiecm/gateway/v3/bridge/url` | Update the bridge URL. |
| `POST` | `/api/hiecm/gateway/v3/sessions` | Generate access token. |
| `GET` | `/api/hiecm/subscription-requests/v3/{subscriptionID}` | Fetch his/her subscription details by subscription ID. |
| `POST` | `/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/approve` | Approve subscription request. |
| `POST` | `/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/deny` | Deny subscription request. |
| `POST` | `/api/hiecm/subscription-requests/v3/disable/{subscriptionID}` | Disable the subscription by subscription ID. |
| `POST` | `/api/hiecm/subscription-requests/v3/enable/{subscriptionID}` | Enable the subscription by subscription ID. |
| `POST` | `/api/hiecm/subscription-requests/v3/hiu/care-context/on-notify` | Respond to /api/v3/hiu/subscription/notify. |
| `POST` | `/api/hiecm/subscription-requests/v3/hiu/on-notify` | Respond to /subscription-requests/hiu/notify. |
| `POST` | `/api/hiecm/subscription-requests/v3/init` | Initiate subscription request. |
| `PUT` | `/api/hiecm/subscription-requests/v3/patients/{subscriptionID}` | Edit the subscription details. |
| `GET` | `/api/hiecm/subscription-requests/v3/request/{subscriptionRequestId}` | Fetch his/her subscription details by subscription REQUEST-ID. |
| `GET` | `/api/hiecm/subscription-requests/v3/requests` | Fetch his/her subscription requests details. |
| `POST` | `/api/v3/hiu/hiecm/subscription-requests/on-init` | This is a callback API for /api/hiecm/subscription-requests/v3/init. |
| `POST` | `/api/v3/hiu/subscription-requests/hiu/notify` | This is a callback API when a subscription request is approved or denied. |
| `POST` | `/api/v3/hiu/subscription/notify` | This is a callback API to notify the subscribed HIU when a care context is link… |
## Headers
| Header | What it is |
| --- | --- |
| `REQUEST-ID` | Unique UUID for track the end to end request transaction |
| `TIMESTAMP` | Actual time of the request was initiated, ISO 8601 represents date and time by starting with the year, follow… |
| `X-CM-ID` | Suffix of the consent manager to which the request was intended |
| `X-AUTH-TOKEN` | JWT Authentication token which was issued by ABDM after successful validation of username and password |
| `X-HIU-ID` | Identifier of the health information user to which the request was intended |
## A request, in full
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/gateway/v3/bridge-service/serviceId/{service-id} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx'
```

View File

@ -0,0 +1,484 @@
# HIE-CM p3 build
Scaffolds an ABDM p3 integration one journey at a time. It covers reading, approving, denying, enabling, disabling and updating the patient's subscriptions and subscription requests, and the subscription request and notifications on the health locker side.
## How this skill runs
Every journey below is an OODA loop, not a recipe: observe the actual state (last response, last error), orient against the step matched below, decide the cheapest next action, act, and return to observe. A step is done only when its exit condition is observed against the sandbox, never because it "should have worked."
Loop limit: 8 passes per step. Hitting the limit is an escalation: state what was observed, what was tried, and which operation page to read, then ask one question.
## Before the first journey, when the codebase already exists
Skip this section only for a system that does not exist yet. Otherwise it runs first, and its exit condition is a written plan, not a call.
### In plain words
Most ABDM integrations are not new systems. They are a hospital management
system, a laboratory system or a clinic application that already has patients,
visits, records, a login, an HTTP client and a way of keeping secrets. Every
ABDM journey has to land somewhere inside that, and a journey built before the
codebase has been read lands in the wrong place: a second HTTP client beside the
first, an ABHA column on the wrong table, a callback route the reverse proxy
never forwards.
So the first loop is not a journey. It is a survey of the system as it is, and
its exit condition is a written plan that names, for every ABDM touchpoint, the
file it will live in. The journeys then build against that plan rather than
against the specification's idea of a fresh codebase.
### Before you start
- The repository, checked out, with permission to read all of it. A survey of
half a codebase produces a plan for half a system.
- The answers to the deployment interview. The
code says what the system is. Only the integrator can say what the
deployment is, and the two together decide which journeys are built at all.
- The practices that hold across every call, which
the plan has to leave room for.
### What happens
This is one loop with a limit of eight passes over the codebase. Each pass
reads live state only, which here means the files themselves, never a README's
description of them and never an assumption carried from a similar system.
**Observe.** Inventory the system from its manifests and its tree, not from its
documentation. Record file paths for each of these, or record that none exists:
| Find | Where it usually shows | Why ABDM needs it |
|---|---|---|
| Languages and runtime versions | package manifests, lock files, toolchain files | Every generated call has to be idiomatic here |
| Build, run and test commands | manifests, CI configuration, a Makefile | Each journey's exit condition becomes a test that runs the same way |
| The frontend and backend split, and how they talk | the top level tree, an API client in the frontend, route definitions in the backend | The counter screens go in the frontend; every ABDM call goes through the backend |
| The outbound HTTP client the backend already uses | the dependency list, a shared client module | ABDM calls reuse it, or a second one appears and the two drift |
| How secrets and configuration reach the process | environment loading, a vault client, a config file | The client id, the secret and the access token travel the same way |
| The patient model, and the identifier fields it already carries | the schema, the ORM models, the migrations | The ABHA number and address become columns beside the existing identifiers, not a new table |
| The visit, encounter or record model | the same places | A care context maps to one of these, and the plan has to say which |
| Where inbound HTTP is routed and authenticated, and whether the deployment has a public URL | the router, the middleware, the reverse proxy configuration, the deployment manifests | ABDM calls back, and a callback route that nothing forwards is silence nobody notices |
| Existing cryptography helpers | a security or crypto module, the dependency list | The RSA encryption of identifiers reuses them |
| Where errors are shown to a user, and where they are logged | the frontend's error surface, the logging setup | A refused call has to land on the screen the person is looking at, and never log a token |
**Orient.** Map each ABDM touchpoint onto that inventory. Where the map is
exact, write the file path. Where it is not, write two candidates and say what
would decide between them. The common ambiguities: two places that could hold
the patient identifier, an HTTP client in the frontend and none in the backend,
a monorepo with several services and no obvious owner for callbacks.
Then read the interview answers against the inventory. A government integrator
gets the demographic route; a facility with no public URL gets no callback
driven journey until it has one; a desk with a fingerprint reader gets the
biometric method. The route set is the intersection of what the deployment
allows and what the code can host.
**Decide.** Order the journeys. The token comes first because every other call
needs it: the gateway session token, or for M4 the bearer token the registry calls
carry. Then the journey whose exit condition can be observed with
the least new code, usually a profile read for a patient who already holds an
ABHA. Creation and linking come after, because each depends on state the earlier
ones produce. For each journey, name the files it will touch and the test that
proves its exit condition.
**Act.** Write the plan. It is a file in the repository, at the path the
integrator names or at the root as `abdm-integration-plan.md`, and it is the
only output of this loop. Then return to observe once, reading the plan against
the tree, to confirm every path in it exists or is marked as new.
If the limit is reached with questions still open, escalate: name what was
found, name the two candidates that could not be separated, point at this
atom, and ask one question.
### How you know it worked
The plan exists and answers every row of the inventory table with a file path,
or with the words none, add at, followed by a path. No row is blank and no row
says to be decided.
Every ABDM touchpoint in the plan names one file, or names two candidates and
the observation that would choose between them. Every journey in the plan names
the test that proves its exit condition and the command that runs it.
Open the plan beside the tree. Every path it names resolves, or is marked as
new. That is the observation that ends this loop, and the first journey does
not start until it has been made.
### When it goes wrong
- **The plan puts the HTTP client in the frontend.** The survey found the
frontend's API client and stopped. Every ABDM call carries a secret, so it
goes through the backend, and the plan has to say which backend module.
- **Two patient tables, and the plan picked one.** Orient produced one
hypothesis where it needed two. Name both, and name the query that shows
which one the visit model points at.
- **The plan has a callback route and the deployment has no public URL.** The
inventory row on inbound routing was answered from the router rather than
from the deployment. Mark every callback driven journey as blocked on a URL,
and build the ones that are not.
- **The survey read the README and not the tree.** A README describes the
system somebody meant to build. The plan is for the one that exists.
- **Eight passes and the plan is still incomplete.** Escalate with the rows
that are answered, the rows that are not, this atom, and one question about
the row that blocks the most journeys.
From `shared.concept.survey-an-existing-codebase`.
## Journeys
### Subscription request and notifications, HIU side (`p3-subscription-hiu`)
**Act: the calls in this journey, in order**
#### 1. Initiate subscription request (`p3_post_subscription_requests_v3_init`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/init \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"subscription": {
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"patient": {
"id": "<ABHA_ADDRESS>"
},
"hiu": {
"id": "INDIA_HIU",
"name": "INDIA HIU",
"type": "HIU"
},
"hips": [
{
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
}
],
"categories": [
"LINK"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
}
}
}'
```
#### 2. Receive the HIU subscription requests on init (`p3_post_v3_hiu_hiecm_subscription_requests_on_init`)
Inbound to your bridge at `/api/v3/hiu/hiecm/subscription-requests/on-init`. Acknowledge it and continue.
#### 3. Notify subscription requests HIU (`p3_post_v3_hiu_subscription_requests_hiu_notify`)
Inbound to your bridge at `/api/v3/hiu/subscription-requests/hiu/notify`. Acknowledge it and continue.
#### 4. Answer the subscription request notification (`p3_post_subscription_requests_v3_hiu_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/hiu/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"subscriptionRequestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
},
"error": {
"code": "ABDM-1001",
"message": "No data found"
},
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
#### 5. Notify HIU subscription (`p3_post_v3_hiu_subscription_notify`)
Inbound to your bridge at `/api/v3/hiu/subscription/notify`. Acknowledge it and continue.
#### 6. Answer the care context subscription notification (`p3_post_subscription_requests_v3_hiu_care_context_on_notify`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/hiu/care-context/on-notify \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgement": {
"status": "OK",
"eventId": "3c2f0e59-8388-4698-9fe6-05db67aeac46"
},
"error": {
"code": "ABDM-1001",
"message": "No data found"
},
"response": {
"requestId": "f29f0e59-8388-4698-9fe6-05db67aeac46"
}
}'
```
**Exit condition (Observe until this is true)**
Each call you made in this journey answered by its callback, with `response.requestId` equal to the `REQUEST-ID` you sent on that call. The 202 on a call is receipt, not the outcome: the outcome is in the callback's body.
- `POST /api/hiecm/subscription-requests/v3/init` is answered by `POST /api/v3/hiu/hiecm/subscription-requests/on-init` on your bridge.
### Subscription approval and management, PHR side (`p3-subscription-phr`)
**Act: the calls in this journey, in order**
#### 1. Approve subscription request (`p3_post_subscription_requests_v3_request_id_approve`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/approve \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"isApplicableForAllHIPs": true,
"includedSources": [
{
"hiTypes": [
"Prescription",
"DiagnosticReport",
"OPConsultation",
"DischargeSummary",
"ImmunizationRecord",
"HealthDocumentRecord",
"WellnessRecord",
"Invoice"
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "www.abdm.gov.in"
},
"categories": [
"LINK",
"DATA"
],
"period": {
"from": "2025-01-09T09:00:00.000Z",
"to": "2124-12-31T09:00:00.000Z"
}
}
],
"excludedSources": []
}'
```
#### 2. Deny subscription request (`p3_post_subscription_requests_v3_request_id_deny`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/{subscriptionRequestId}/deny \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"reason": "Subscription denied."
}'
```
#### 3. Fetch his/her subscription requests details (`p3_get_subscription_requests_v3_requests`)
```bash
curl --request GET \
--url "https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/requests?limit=5&offset=5&status=ALL" \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 4. Edit the subscription details (`p3_put_subscription_requests_v3_patients_subscription_id`)
```bash
curl --request PUT \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/patients/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
"hiuId": "INDIA_HIU",
"subscriptionEditAndApprovalRequest": {
"isApplicableForAllHIPs": true,
"includedSources": [
{
"hiTypes": [
"Prescription"
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"hip": {
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
},
"categories": [
"LINK"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
},
"status": "SUCCESS"
}
],
"excludedSources": [
{
"hiTypes": [
"Prescription"
],
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"hip": {
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
},
"categories": [
"LINK"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
},
"status": "SUCCESS"
}
]
}
}'
```
#### 5. Disable the subscription by subscription ID (`p3_post_subscription_requests_v3_disable_subscription_id`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/disable/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 6. Enable the subscription by subscription ID (`p3_post_subscription_requests_v3_enable_subscription_id`)
```bash
curl --request POST \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/enable/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 7. Fetch his/her subscription details by subscription REQUEST-ID (`p3_get_subscription_requests_v3_request_request_id`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/request/{subscriptionRequestId} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
#### 8. Fetch his/her subscription details by subscription ID (`p3_get_subscription_requests_v3_subscription_id`)
```bash
curl --request GET \
--url https://dev.abdm.gov.in/api/hiecm/subscription-requests/v3/{subscriptionID} \
--header 'Authorization: Bearer <ACCESS_TOKEN_FROM_SESSIONS_CALL>' \
--header 'REQUEST-ID: 18235d89-cb13-479d-ad71-7a57d5f669a8' \
--header 'TIMESTAMP: 2022-10-06T15:10:00.587Z' \
--header 'X-CM-ID: sbx' \
--header 'X-AUTH-TOKEN: <TOKEN>'
```
**Exit condition (Observe until this is true)**
A 200 whose body matches:
```json
{
"subscriptionId": "f29f0e59-8388-4698-9fe6-05db67aeac46",
"purpose": {
"text": "Care Management",
"code": "CAREMGT",
"refUri": "https://abc.def.in"
},
"dateCreated": "2021-09-28T12:30:08.573Z",
"status": "GRANTED",
"dateGranted": "2021-09-28T12:30:08.573Z",
"patient": {
"id": "<ABHA_ADDRESS>"
},
"requester": {
"id": "<ABHA_ADDRESS>",
"name": "ABDM_HIU",
"type": "HIU"
},
"includedSources": [
{
"hip": {
"id": "INDIA_HIP",
"name": "INDIA HIP",
"type": "HIP"
},
"categories": [
"LINK"
],
"hiTypes": [
"Prescription"
],
"period": {
"from": "2024-05-09T10:34:00.389Z",
"to": "2024-05-09T10:34:00.389Z"
},
"status": "GRANTED"
}
]
}
```
## Where the detail is
- Every operation, with its body fields and responses: /docs/hiecm/v3/api/p3
- Error codes: /docs/hiecm/v3/api/p3/errors

View File

@ -0,0 +1,77 @@
---
name: abdm-p4
description: Use when building, debugging or testing ABDM P4, health lockers: setting up a locker and listing the lockers and requests on an ABHA address.
type: skill
domain: p4
agent_consumers:
- abdm-integration-agent
- abdm-call-debugger
requires:
- phr-login
produces:
- locker-record
can_execute: true
can_orchestrate: false
---
# ABDM P4, health lockers
Generated from the ABDM Developer Portal on 2026-09-16, catalogue version 2026.09.16. Every fact below comes from a page in that portal, which is the place to look when this file does not carry enough.
This file is a snapshot. Re-download the whole folder from https://docs.abdm.gov.in/skills/abdm-p4/ when it is older than the work you are doing: this router and every file under references/ that it links to. Fetching this file alone leaves those links pointing at files you do not have.
If the abdm-docs MCP server is connected, trust its answers over this file: it serves the current catalogue and stamps every response with its catalogue_version, which you can compare against the version above.
## What you can do with P4
- Locker
What it cannot do yet matters as much. Read **Before anything else** below before assuming a capability is one endpoint away.
## Try asking
- "Set up a health locker for this user"
- "What has to be in place before my first P4 call?"
- "My P4 call failed. Here is the response: what is wrong, and how do I fix it?"
Loaded with no task? Say in three lines what this skill does. Offer the prompts above. Then ask what the person is building, and whether the code for it exists yet.
## What is in this folder
- **Scaffold.** Survey the codebase first when one exists, then build it flow by flow against the sandbox, as a loop that ends when the step's exit condition holds rather than on a call returning 200. [references/scaffold.md](references/scaffold.md)
- **Integrate.** 9 operations, with their hosts and headers. [references/integrate.md](references/integrate.md)
- **Debug.** The specification's examples return no error code for this module. [references/debug.md](references/debug.md)
This file is the map. Each line above is a file beside it, opened one at a time rather than read through.
## Before anything else
- Treat every request and response shape in this skill as unconfirmed until the sandbox has answered you. Check a response before you rely on its shape.
## Practices that hold across every call
- Read the body, not only the status. A refusal often names the field in its body while the status says nothing useful.
- When ABDM publishes a value, read it rather than hard coding what it currently says. That covers a parameter, such as the encryption algorithm the certificate endpoints return beside the key, and it covers an enumeration. The registries publish theirs as master data calls: councils, courses, colleges, states and districts on the HPR, and facility types and LGD codes on the HFR. A table typed into your source goes stale silently. Purposes and HI types have no master data call: take them from the enumeration in the specification of the call you are making. Refuse to act on a published value you do not recognise rather than falling back to a default.
- Read every field in a response, not the one you came for. The M1 certificate call returns the encryption algorithm next to the key.
- Prove an assumption against a call that is able to disagree with you. A call that refuses every input with one message cannot tell you which input was right.
- Suspect the transport before the data. When a call refuses a value you believe in, check the encryption, the headers and the clock before you doubt the value.
- Do not carry an encryption path from one module to another. Read the certificate and the algorithm from the registry you are calling.
- Never log a sensitive value before you encrypt it, and never send one to a remote service to be encrypted. Both move the leak rather than removing it.
- Generate a fresh REQUEST-ID for every call and log it before sending. Once a call has failed it is the only handle on it.
- Check the host on any sample before you copy it. A request copied whole can be correct in every respect except where it is pointed, and that failure looks like credentials.
- Do not validate an identifier more strictly than the platform does. A schema that types a field as a UUID is not a promise that every value is one. Refusing a value ABDM would have accepted turns your own client into the thing that broke.
- Read a plural response as plural. A verification returns an accounts array and a consent request can produce more than one artefact. Store the collection and decide from its length.
- A documented callback path is not a documented callback payload. Log the whole body on arrival before you parse it, so a handler written against an assumed shape fails where you can see it.
- Decode a token before you use it. A call that hands you a token has not necessarily handed you the token the next call wants, and the claims are base64 that need no library to read.
- When a journey carries the same parameter through two calls, check what each call's specification asks for rather than carrying the first value forward.
- Do not infer which path an identifier belongs to from where that identifier is most discussed. Send every identifier to the lookup first and let the answer pick the path, so nobody who already holds an account is sent to create a second.
- Do not match an error code with string equality. A code may come back bare, as `ABDM-1001`, or with a trailing `: ` separator, as `ABDM-1001: `. Match on the code itself and tolerate the separator.
- Handle a failure that carries no body. Check for an empty body before you parse, or your client throws on the simplest failure there is.
- Back off on an authentication failure rather than retrying, and never loop a credential check.
- Send TIMESTAMP in UTC, ISO-8601 with milliseconds and a trailing Z, as in `2022-10-06T15:10:00.587Z`.
- Cache a public certificate with a validity window rather than forever. A rotation fails every encrypted call at once, and a cache with no expiry cannot recover on its own.
## Where the detail is
- Every endpoint, with its body fields and responses: /docs/hiecm/v3/api/p4
- Every error code across modules: /docs/hiecm/v3/reference/error-codes
- Terms: /docs/hiecm/v3/getting-started/glossary

View File

@ -0,0 +1,9 @@
# Debug P4, health lockers
The codes this module can return, with the message and the operation that returns each.
## Errors
The P4, health lockers specification's examples return no error code yet. That is a gap in the specification, not a promise that this module cannot fail.
A code you meet that is not above is one the specifications do not carry yet. Read the code together with the message: a code can appear twice with different meanings.

Some files were not shown because too many files have changed in this diff Show More