Kulcare Docs

ABDM M1/M2/M3 Live Testing Log

Started: 2026-07-28 Tester: Simmi (manual), assisted by Claude Environment: ABDM sandbox (https://dev.abdm.gov.in) Bridge: SBX_001751, client_id SBX_001751 Facility HIU IDs: IN1810000370 (HIP+HIU+PHR), IN0410000202 (HIP) Test patient: 019f82e1-3d73-7a4a-88c8-da423b257e53 Test user: phone 9815707711 (Identity user 019e4401-5f62-73f2-8053-88eba92084b1) ngrok: balanced-loudly-midge.ngrok-free.app


Status Legend

SymbolMeaning
✅Works end-to-end
⚠️Works with caveats / partial
❌Broken or missing
🚧Implementation needed
🔍Not yet tested

M1 — ABHA Number Creation

Spec endpoints (from docs/abdm/specs/M1/ABHA_API_V3_SWAGGER_31_07_2025.yaml)

All M1 endpoints require the gateway Bearer token in Authorization header (NOT X-token — gateway token is for the unauthenticated pre-login phase). The X-token is only needed for /profile/account* (post-login).

#EndpointAuth headerBody / params
1POST /abha/api/v3/enrollment/request/otpAuthorization: Bearer <gateway>Aadhaar: scope: ['abha-enrol'], loginHint: 'aadhaar', loginId: <encrypted_aadhaar>, otpSystem: 'aadhaar'
2POST /abha/api/v3/enrollment/enrol/byAadhaarAuthorization: Bearer <gateway>{ authData: { authMethods: ['otp'], otp: { txnId, otpValue: <encrypted_otp>, mobile } }, consent: { code: 'abha-enrollment', version: '1.4' } }
3POST /abha/api/v3/enrollment/auth/byAbdmAuthorization: Bearer <gateway>{ scope: ['abha-enrol', 'mobile-verify'], authData: { authMethods: ['otp'], otp: { txnId, otpValue: <encrypted_otp> } } }
4POST /abha/api/v3/profile/account/request/emailVerificationLinkAuthorization: Bearer <X-token>(body per swagger)
5GET /abha/api/v3/enrollment/enrol/suggestionAuthorization: Bearer <gateway>Header Transaction_Id: <txnId>, REQUEST-ID, TIMESTAMP
6POST /abha/api/v3/enrollment/enrol/abha-addressAuthorization: Bearer <gateway>{ txnId, abhaAddress, preferred: 1 }
7GET /abha/api/v3/profile/accountAuthorization: Bearer <X-token>Headers: X-token: Bearer <x_token>, REQUEST-ID, TIMESTAMP
8GET /abha/api/v3/profile/account/abha-cardAuthorization: Bearer <X-token>Headers: X-Token: Bearer <x_token>, REQUEST-ID, TIMESTAMP — response is image/png binary

Encryption: Aadhaar number and OTP must be RSA-encrypted using the PHR public key (see EncryptPhrData.perform with key_type: :enrollment for Aadhaar).

Our backend coverage

Spec endpointOur implementationStatus
/enrollment/request/otp (mobile OTP)Phr::Registration::Enrolment::RequestOtp.perform — scope: ['abha-address-enroll', 'mobile-verify'], loginHint: 'mobile-number', otpSystem: 'abdm'✅
/enrollment/request/otp (Aadhaar OTP)Hard-coded to mobile-verify. No Aadhaar-mode support.❌ Missing
/enrollment/verify (mobile OTP)Phr::Registration::Enrolment::VerifyOtp.perform — scope: ['abha-address-enroll', 'mobile-verify']✅
/enrollment/verify (Aadhaar OTP)Hard-coded to mobile-verify scope.❌ Missing
/enrollment/enrol/byAadhaarNot implemented.🚧 Missing
/enrollment/auth/byAbdmNot implemented.🚧 Missing
/profile/account/request/emailVerificationLinkPhr::Profile::Email::RequestVerificationLink✅ Exists
/enrollment/enrol/suggestionPhr::Registration::Enrolment::Suggestion.perform → ABDM /enrollment/suggestion⚠️ Wrong URL (spec says enrol/suggestion, we call enrollment/suggestion)
/enrol/abha-addressPhr::Registration::Enrolment::Enrol.perform → ABDM /enrollment/enrol⚠️ Wrong URL (spec says enrol/abha-address, we call enrollment/enrol)
/profile/accountNot implemented in PHR. /phr/patients/profile exists but uses different ABDM endpoint.🚧 Missing
/profile/account/abha-cardNot implemented. fetch_phr_card_service.rb exists but unclear if it uses this endpoint.🚧 Missing

FE coverage

FE fileEndpointStatus
AbhaRegistrationForm.tsxsend_otp → verify_otp → suggest → check_address → enrol (mobile-OTP path)✅
LoginForm.tsxabha_number_aadhaar/request_otp → abha_number_aadhaar/verify_otp (login mode, NOT creation)✅ (login only)
AbhaRegistrationForm.tsxNo Aadhaar-based creation flow❌ Missing

Verhoef algorithm / Aadhaar validation

Not implemented anywhere. Per spec:

  • Last digit is checksum
  • verhoeff_algorithm validator needed client-side and/or server-side
  • Aadhaar must NOT be persisted (compliance)

Consent before Aadhaar entry (per spec)

Not implemented. Per spec, FE must capture explicit user consent before collecting Aadhaar. Consent language referenced: docs/abdm/specs/M1/Consent_Language_ABDM_1_25c394153b.png


M1 — Ready-to-run curl recipes

A. Get gateway token (needed for all enrollment calls)

# Internal: we already have a backend service that fetches gateway tokens
# Use rails console: Phr::FetchGatewayTokenV3.perform
# Or call ABDM directly (see /abha/api/v3/gateway/v3/wso2/auth/token if exposed)

B. Mobile-OTP creation (existing flow, end-to-end happy path)

# 1. Request OTP
curl -X POST https://dev.abdm.gov.in/abha/api/v3/phr/app/enrollment/request/otp \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": ["abha-address-enroll", "mobile-verify"],
    "loginHint": "mobile-number",
    "loginId": "<RSA-ENCRYPTED-MOBILE>",
    "otpSystem": "abdm"
  }'
# → returns { txnId, message }

# 2. Verify OTP (use txnId from step 1)
curl -X POST https://dev.abdm.gov.in/abha/api/v3/phr/app/enrollment/verify \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "authData": {
      "authMethods": ["otp"],
      "otp": {
        "txnId": "<TXN_ID>",
        "otpValue": "<RSA-ENCRYPTED-OTP>"
      }
    },
    "scope": ["abha-address-enroll", "mobile-verify"]
  }'
# → returns NEW txnId for use in steps 3/4

# 3. Get ABHA address suggestions
curl -X GET "https://dev.abdm.gov.in/abha/api/v3/enrollment/enrol/suggestion" \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "Transaction_Id: <TXN_ID>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
# → returns { txnId, abhaAddressList: [...] }

# 4. Enrol chosen ABHA address
curl -X POST https://dev.abdm.gov.in/abha/api/v3/enrollment/enrol/abha-address \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "txnId": "<TXN_ID>",
    "abhaAddress": "<CHOSEN_ADDRESS>@sbx",
    "preferred": 1
  }'
# → returns success — ABHA account created

C. Aadhaar-OTP creation (NOT YET IMPLEMENTED in our backend — needs new endpoints)

# 1. Request Aadhaar OTP
curl -X POST https://dev.abdm.gov.in/abha/api/v3/enrollment/request/otp \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "txnId": null,
    "scope": ["abha-enrol"],
    "loginHint": "aadhaar",
    "loginId": "<RSA-ENCRYPTED-AADHAAR>",
    "otpSystem": "aadhaar"
  }'
# → returns { txnId, message }

# 2. Verify Aadhaar OTP + create ABHA (returns 14-digit number!)
curl -X POST https://dev.abdm.gov.in/abha/api/v3/enrollment/enrol/byAadhaar \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "authData": {
      "authMethods": ["otp"],
      "otp": {
        "txnId": "<TXN_ID>",
        "otpValue": "<RSA-ENCRYPTED-OTP>",
        "mobile": "<USER_MOBILE_NOT_ENCRYPTED>"
      }
    },
    "consent": {
      "code": "abha-enrollment",
      "version": "1.4"
    }
  }'
# → returns NEW txnId, plus ABHA profile

# 3. (Only if mobile ≠ Aadhaar-linked mobile) verify mobile update
curl -X POST https://dev.abdm.gov.in/abha/api/v3/enrollment/auth/byAbdm \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": ["abha-enrol", "mobile-verify"],
    "authData": {
      "authMethods": ["otp"],
      "otp": {
        "txnId": "<TXN_ID>",
        "otpValue": "<RSA-ENCRYPTED-OTP>"
      }
    }
  }'

# 4. Suggest ABHA addresses
curl -X GET "https://dev.abdm.gov.in/abha/api/v3/enrollment/enrol/suggestion" \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "Transaction_Id: <TXN_ID>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"

# 5. Enrol chosen ABHA address
curl -X POST https://dev.abdm.gov.in/abha/api/v3/enrollment/enrol/abha-address \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -H "Content-Type: application/json" \
  -d '{
    "txnId": "<TXN_ID>",
    "abhaAddress": "<CHOSEN_ADDRESS>@sbx",
    "preferred": 1
  }'

D. View profile + Download ABHA card (post-login, uses X-token)

# Get profile details (14-digit ABHA number, name, DOB, etc.)
curl -X GET "https://dev.abdm.gov.in/abha/api/v3/profile/account" \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "X-token: Bearer <X_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"

# Download ABHA card image (PNG)
curl -X GET "https://dev.abdm.gov.in/abha/api/v3/profile/account/abha-card" \
  -H "Authorization: Bearer <GATEWAY_TOKEN>" \
  -H "X-Token: Bearer <X_TOKEN>" \
  -H "REQUEST-ID: $(uuidgen)" \
  -H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
  -o abha-card.png

M2 — HIP-Initiated Linking

Status: testing in progress (see previous testing guide)

  • Auto link token generation after M1 verification — wired in perform_auto_link_token
  • 5 missing v3 callback routes added
  • Care context linking & on_carecontext callback — 🔍 not yet tested end-to-end

Status: testing in progress (see previous testing guide)

  • 3 consents in requested status
  • Just-fixed: ACK format (array), frequency value (1), auto-fetch after GRANTED, missing callback routes
  • 🔍 not yet tested end-to-end with patient approval in ABHA app

On this page