HIP Identity & Linking (M1 root + M1 Identity/Linking)

What this covers

This document describes the facility-side (HIP) surface of orvo-abha that deals with a patient's ABHA (Ayushman Bharat Health Account) identity and with connecting ("linking") a patient's hospital records — visits, prescriptions, discharge summaries, etc., called care contexts — to that ABHA. In plain terms: when a walk-in patient visits a hospital or clinic that uses this backend, these are the APIs that (a) create a brand-new ABHA for them on the spot using their Aadhaar, (b) look up and re-use an ABHA they already have, (c) let staff search ABDM's national facility directory, and (d) establish the link between the patient's ABHA and their medical record at this facility, so that later on a doctor with the patient's consent can pull those records through ABDM.

There are two directions of traffic throughout this document:

Every inbound callback in this document is called directly by ABDM's infrastructure over the public internet (no user session, no bearer token) — access control for these is IP/network-level, not application-level, and the handlers must acknowledge within ~5 seconds (HTTP 202) before doing any real work, per ABDM's own timeout contract.

Two authentication conventions recur throughout:


Facility Enrollment — Aadhaar-only ABHA creation (M1 root)

Plain-English: this is the "create an ABHA on the spot" flow a receptionist or nurse runs when a walk-in patient has no ABHA yet, has their Aadhaar card, and consents to using it. It is doctor/staff-assisted — the patient is physically present and reads OTPs off their own phone — as opposed to the separate PHR-app self-service enrollment (self-declared/mobile-based) which is out of scope here. All routes are mounted at /v1/hip/enrollment.

POST /v1/hip/enrollment/request

Create Aadhaar OTP. Sends an ABHA-creation OTP to the mobile number linked to the patient's Aadhaar.

{
  "aadhaarNumber": "234567890123"
}
{
  "success": true,
  "message": "OTP requested successfully",
  "data": {
    "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
    "message": "OTP sent to Aadhaar-linked mobile"
  }
}

POST /v1/hip/enrollment/verify

Create ABHA by verifying Aadhaar OTP. The patient reads the OTP that landed on their Aadhaar-linked phone; verifying it creates the ABHA account via KYC.

{
  "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
  "otpValue": "123456",
  "mobile": "9876543210"
}

Note mobile is required here even if it's the same number the Aadhaar OTP was sent to — ABDM's enrol/byAadhaar contract mandates a communication mobile regardless.

{
  "success": true,
  "message": "Enrollment verified successfully",
  "data": {
    "token": "eyJhbGciOi...",
    "expiresIn": 1800,
    "refreshToken": "eyJhbGciOi...",
    "refreshExpiresIn": 1209600,
    "ABHAProfile": {
      "firstName": "Rajesh",
      "lastName": "Kumar",
      "dob": "15-08-1990",
      "gender": "M",
      "mobile": "9876543210",
      "ABHANumber": "12-3456-7890-1234",
      "phrAddress": []
    }
  }
}

POST /v1/hip/enrollment/mobile/request

Mobile Update — send OTP. Only needed when the mobile given at verify differs from the Aadhaar-linked one.

{
  "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
  "mobile": "9123456789"
}

POST /v1/hip/enrollment/mobile/verify

Mobile Update — verify OTP. Confirms the alternate communication mobile so ABDM releases full tokens.

{
  "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
  "otpValue": "654321"
}

GET /v1/hip/enrollment/address/suggestions/:txnId

Get ABHA address suggestions. Since Aadhaar KYC already supplied the patient's demographics, only the txnId is needed to ask ABDM for candidate name@sbx-style handles.

{
  "success": true,
  "message": "ABHA address suggestions fetched successfully",
  "data": {
    "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
    "abhaAddressList": ["rajesh.kumar", "rajesh.kumar90", "rajeshk1990"]
  }
}

POST /v1/hip/enrollment/address

Create ABHA address. Final step of enrollment — assigns the chosen (or custom) address to the new account.

{
  "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
  "abhaAddress": "rajesh.kumar",
  "preferred": 1
}

POST /v1/hip/enrollment/email/verify

Send email verification link. Optional profile-completion step.

GET /v1/hip/enrollment/profile

Get enrolled ABHA profile. Fetches the freshly created profile back, with address synthesized server-side by joining ABDM's separate addressLine1/addressLine2 fields.

GET /v1/hip/enrollment/profile/qr

Get enrolled ABHA profile QR code. Returns the ABHA QR as a base64 data URI (data:image/png;base64,...), convenient for direct <img> embedding.

GET /v1/hip/enrollment/profile/card

Get enrolled ABHA card. Same idea, returns the printable ABHA card image as a base64 data URI.


Facility Login — linking an existing ABHA (M1 root)

Plain-English: this is for the far more common case — the patient already has an ABHA from a previous visit or from the ABDM/PHR app — and staff just need to look it up and attach it to today's visit. All routes are mounted at /v1/hip/login.

There are two entry points ("login hints"):

POST /v1/hip/login/request/otp

Request login OTP.

{
  "loginHint": "abha-number",
  "loginId": "12345678901234",
  "otpMethod": "abdm"
}

otpMethod picks the OTP channel — aadhaar (Aadhaar-linked mobile) or abdm (ABHA/ABDM-registered mobile); defaults to abdm if omitted. It's ignored for the abha-address hint. Alternative request (abha-address hint):

{
  "loginHint": "abha-address",
  "loginId": "rajesh.kumar@sbx"
}

POST /v1/hip/login/verify/otp

Verify login OTP.

{
  "loginHint": "abha-number",
  "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11",
  "otp": "123456",
  "otpMethod": "abdm"
}

POST /v1/hip/login/verify/user

Verify user (abha-number only). Picks one specific ABHA address from the linked accounts under a multi-address ABHA number.

{
  "abhaAddress": "rajesh.kumar@sbx",
  "txnId": "b1b6c3e7-5e2c-4c7f-9f02-9c8fbc3caa11"
}

GET /v1/hip/login/profile

Get profile for the logged-in ABHA.

GET /v1/hip/login/profile/card

Get ABHA card for the logged-in ABHA.


Gateway / Discovery utilities

Plain-English: a grab-bag of read-mostly endpoints for looking things up in ABDM's national directories (registered facilities/providers, government health programs, health lockers) and for managing this deployment's own registration as a HIP/HIU bridge service with ABDM. All routes are mounted at /v1/gateway. None of these carry patient data — they are directory/config lookups.

GET /v1/gateway/bridge-services

Fetch this deployment's bridge + registered services. Confirms what HIP/HIU/HEALTH_LOCKER/PHR roles and callback endpoints ABDM currently has on file for our bridge.

{
  "success": true,
  "data": {
    "bridge": {
      "id": "IN0310000702",
      "name": "Orvo Technologies Private Limited",
      "url": "https://api.orvo.app/api/v3",
      "active": true,
      "blocklisted": false
    },
    "services": [
      {
        "id": "IN0310000702_1",
        "name": "Orvo_HIP",
        "types": ["HIP"],
        "endpoints": {
          "hipEndpoints": [
            {
              "use": "https://api.orvo.app/api/v3",
              "connectionType": "HTTP",
              "address": "https://api.orvo.app/api/v3"
            }
          ]
        },
        "active": true
      }
    ]
  }
}

PUT /v1/gateway/bridge-services (POST alias also accepted)

Register/update a bridge service. Adds or updates a HIP/HIU/Health Locker/PHR service under this deployment's ABDM bridge. The POST alias exists purely because some CDN/WAF layers block PUT; the upstream ABDM call is still a PUT.

{
  "bridgeId": "SBXID_049344",
  "serviceId": "IN0310000754",
  "name": "Orvo_HIU",
  "isHip": true,
  "isHiu": false,
  "isHealthLocker": false,
  "isPhr": false,
  "endpoints": {
    "hipEndpoints": [
      {
        "use": "https://api.orvo.app/api/v3",
        "connectionType": "HTTP",
        "address": "https://api.orvo.app/api/v3"
      }
    ]
  },
  "active": true
}

Note: isHealthLocker: true is accepted by this endpoint but ABDM/NHA must separately approve that role before it takes effect.

GET /v1/gateway/bridge-services/:serviceId

Fetch a single bridge service's registration details — role flags, active status, registration/creation/modification timestamps.

{
  "success": true,
  "data": {
    "id": 4821,
    "bridgeId": "IN0310000702",
    "serviceId": "IN0310000702_1",
    "name": "Orvo_HIP",
    "isHip": true,
    "isHiu": false,
    "isHealthLocker": false,
    "isPhr": false,
    "active": true,
    "registerTime": "2026-01-15T10:30:00.000Z",
    "dateCreated": "2026-01-15T10:30:00.000Z",
    "dateModified": "2026-01-15T10:30:00.000Z"
  }
}

GET /v1/gateway/bridge-url

Fetch latest stored bridge callback URL. Purely a local DB read (not a live ABDM call) — returns the most recently persisted callback URL for the active facility, or null if never set.

PATCH /v1/gateway/bridge-url

Update bridge callback URL. Persists a new callback URL locally, then forwards the update to ABDM as the registered callback for that facility's bridge/hipId. Both steps run for every call — the local write happens even if the downstream ABDM call subsequently fails.

GET /v1/gateway/providers

Search the ABDM provider/facility directory.

{
  "success": true,
  "data": [
    {
      "identifier": { "name": "Apollo Hospitals Bengaluru", "id": "IN0310000850" },
      "facilityType": ["HIP"],
      "isHip": true,
      "isHiu": false,
      "isHealthLocker": false,
      "isPhr": false
    }
  ]
}

GET /v1/gateway/providers/:providerId

Fetch a single provider/facility's details.

GET /v1/gateway/government-programs

List government health-program entities registered in ABDM as facility-like entities.

GET /v1/gateway/health-lockers

List entities registered under the HEALTH_LOCKER role, with callback endpoints where available.

PATCH /v1/gateway/bridge-config

Update bridge configuration — acknowledgement only. Bridge clientId/clientSecret credentials are managed out-of-band via environment variables and the encrypt-bridge-secrets script; this endpoint validates the body but otherwise no-ops.


HIP-Initiated Linking

Plain-English: this is the flow where the facility (not the patient) starts the process of connecting a patient's visit records to their ABHA — staff already has the patient's ABHA address on file and wants to attach today's OPD visit to it, without the patient actively "discovering and linking" through their own PHR app. It relies on a link token obtained first, then submits the actual care-context references for linking, then can additionally notify the patient (via PHR app or SMS).

POST /v1/link/token/generate

Generate a link token for a patient, by demographic authentication (name + gender + year of birth + ABHA address/number).

{
  "abhaAddress": "rajesh.kumar@sbx",
  "abhaNumber": "23456789012345",
  "gender": "M",
  "name": "Rajesh Kumar",
  "yearOfBirth": 1990
}
{
  "success": true,
  "message": "Link token generated successfully",
  "data": { "requestId": "123e4567-e89b-12d3-a456-426614174000" }
}

The hub subscribes to a Pusher channel keyed by this requestId and waits for link-token:received/link-token:failed — the actual token arrives later via the inbound callback below.

On the known ABDM-1092 "duplicate request" condition, the service auto-recovers by returning the existing live request instead, possibly already linkTokenReady:

{
  "success": true,
  "data": {
    "requestId": "9f8a7b6c-1234-4c7f-9f02-9c8fbc3caa11",
    "linkTokenReady": true,
    "abhaAddress": "rajesh.kumar@sbx",
    "expiresAt": "2026-07-18T14:30:00.000Z"
  }
}

Inbound ABDM callback — POST /api/v3/hip/token/on-generate-token

ABDM's gateway POSTs the generated link token (or an error) here once it's ready.

{
  "abhaAddress": "rajesh.kumar@sbx",
  "linkToken": "abcdef123456.eyJhbGciOi...",
  "response": { "requestId": "123e4567-e89b-12d3-a456-426614174000" }
}

Request (error): { "error": { "code": "ABDM-1010", "message": "Patient not found" }, "response": { "requestId": "..." } }

Care Context (local read) — /v1/care-context

GET /v1/care-context

List a patient's care contexts on file at this facility. Purely local (no ABDM call) — populates the "which visits/records to link" selection step.

{
  "success": true,
  "data": {
    "careContexts": [
      {
        "id": "cc-001",
        "careContextReference": "TMH-CC-001",
        "display": "Visit 1",
        "hiType": "OPConsultation",
        "linked": false,
        "abhaAddress": "rajesh.kumar@sbx",
        "patientReference": "TMH-PUID-001"
      }
    ]
  }
}

linked only flips true once ABDM's on_carecontext callback confirms that reference. Empty array (not 404) if none on file.

HIP Linking — /v1/hip-linking

POST /v1/hip-linking/patient/links/care-context

Initiate linking of care contexts (ABDM M2 §4.3).

{
  "abhaAddress": "rajesh.kumar@sbx",
  "abhaNumber": "23456789012345",
  "patient": [
    {
      "referenceNumber": "TMH-PUID-001",
      "display": "Rajesh Kumar",
      "careContexts": [{ "referenceNumber": "TMH-CC-001", "display": "Visit 1" }],
      "hiType": "OPConsultation",
      "count": 1
    }
  ]
}

Get all care contexts linked to a patient's ABHA, across every HIP (PHR-side).

{
  "success": true,
  "data": {
    "Patient": {
      "id": "rajesh.kumar@sbx",
      "links": [
        {
          "hip": { "id": "IN0310000702_1", "name": "Orvo Test Hospital", "type": "HIP" },
          "referenceNumber": "TMH-PUID-001",
          "display": "Rajesh Kumar",
          "hiType": "OPConsultation",
          "careContexts": [{ "referenceNumber": "TMH-CC-001", "display": "Visit 1" }],
          "dateCreated": "2026-07-18T10:00:00.000Z"
        }
      ]
    }
  }
}

POST /v1/hip-linking/patient/links/notify

Notify the patient's PHR app that a link is complete.

{
  "notification": {
    "patient": { "id": "rajesh.kumar@sbx" },
    "careContext": { "patientReference": "TMH-PUID-001", "careContextReference": "TMH-CC-001" },
    "hiTypes": ["OPConsultation"],
    "date": "2026-07-18T10:00:00.000Z",
    "hip": { "id": "IN0310000702_1", "name": "Orvo Test Hospital" }
  }
}

POST /v1/hip-linking/patient/links/sms/notify

Notify by SMS instead. Thin passthrough — no link-token lookup, no X-HIP-ID requirement (hip.id supplied directly).

{
  "requestId": "123e4567-e89b-12d3-a456-426614174000",
  "timestamp": "2026-07-18T10:00:00.000Z",
  "notification": {
    "phoneNo": "9876543210",
    "hip": { "id": "IN0310000702_1", "name": "Orvo Test Hospital" }
  }
}

POST /v1/hip-linking/notify-sms

Simplified SMS notify for mobile-only patients with no ABHA address yet — triggers ABDM to text a deep link to create one and self-link.

HIP Linking — inbound callbacks

Inbound ABDM callback — POST /api/v3/link/on_carecontext

Final result of care-context linking (success/failure).

Inbound ABDM callback — POST /api/v3/link/on_notify

Confirms whether the CM processed the patient/links/notify call.

Inbound ABDM callback — POST /api/v3/patients/sms/on-notify

Confirms SMS dispatch.


User-Initiated Linking (UIL)

Plain-English: the opposite direction — the patient actively searches ("discovers") for their records at a chosen facility, then completes an OTP-confirmed link. This backend plays both roles simultaneously: HIU/PHR side (patient-triggered, calls ABDM) and HIP/facility side (receives ABDM's inbound requests). All outbound routes are mounted at /v1/user-linking.

PHR-side (patient-triggered)

POST /v1/user-linking/care-context/discover

Request discovery at a chosen HIP.

{
  "hip": { "id": "cowin_hip_01" },
  "unverifiedIdentifiers": [
    { "type": "MOBILE", "value": "+919876543210" },
    { "type": "ABHA_ADDRESS", "value": "rajesh.kumar@sbx" }
  ]
}

GET /v1/user-linking/care-context/discovery-result

Polling fallback.

POST /v1/user-linking/care-context/init

Initiate the link after selecting discovered care contexts.

{
  "transactionId": "f901b782-bfdf-4224-9f8d-da2cadc20c0d",
  "patient": [
    {
      "referenceNumber": "TMH-PUID-001",
      "display": "Rajesh Kumar",
      "careContexts": [{ "referenceNumber": "TMH-CC-001", "display": "Visit 1" }],
      "hiType": "OPConsultation",
      "count": 1
    }
  ]
}

POST /v1/user-linking/care-context/confirm

Complete the link with the OTP from init.

Facility-side outbound relay calls (called by orvo-hub, NOT ABDM — despite the "on-*" naming)

POST /v1/user-linking/care-context/on-discover, .../on-init, .../on-confirm — these forward the facility's resolved answer to an inbound ABDM discover/init/confirm request onward to ABDM's gateway. No auth middleware applied at the route level; reachable only by trusted internal callers (orvo-hub). All return 202 acks.

GET /v1/user-linking/callback-traces

Diagnostic endpoint listing persisted inbound UIL callbacks paired with our responses, for tracing stuck/failed linking attempts.

User-Initiated Linking — inbound callbacks

All ack 202 Accepted immediately, process in background. Real paths require the /api prefix.

Inbound ABDM callback — POST /api/v3/hip/patient/care-context/discover

ABDM asks the facility to check for matching records. HIP id via X-HIP-ID header (non-fatal). Forwarded onward via on-discover relay.

Inbound ABDM callback — POST /api/v3/hip/link/care-context/init

ABDM instructs the facility to begin the link (send OTP). Forwarded via on-init relay.

Inbound ABDM callback — POST /api/v3/hip/link/care-context/confirm

ABDM delivers the patient's OTP submission (lenient schema — token/linkRefNumber may be nested or flat). Forwarded via on-confirm relay.

Inbound ABDM callback — POST /api/v3/hiu/patient/care-context/on-discover

Confirms discovery result back to our HIU/PHR side; persists result and fires user-linking:on-discovered Pusher event.

Inbound ABDM callback — POST /api/v3/hiu/patient/care-context/on-init

Confirms link-init (OTP dispatched) back to our HIU/PHR side.

Inbound ABDM callback — POST /api/v3/hiu/patient/care-context/on-confirm

Confirms final link-confirm result; linked care contexts become available for LHR sync; Pusher event notifies the patient-facing UI.


Facilities management

Plain-English: internal CRUD for the list of facilities this backend can act as an ABDM HIP/HIU on behalf of. Mounted at /v1/facilities; no auth header at the route level (admin/internal surface).

Example facility object: `{ "id": "a1b2c3d4-...", "mainAppFacilityId": "FAC-1024", "name": "Orvo Multispeciality Hospital", "hfrFacilityId": "IN2910000123", "hipId": "IN0310000702_1", "bridgeId": "IN0310000702", "hipName": "Orvo Multispeciality Hospital", "environment": "sandbox", "active": true, "createdAt": "...", "updatedAt": "..." }