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
| Symbol | Meaning |
|---|---|
| ✅ | 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).
| # | Endpoint | Auth header | Body / params |
|---|---|---|---|
| 1 | POST /abha/api/v3/enrollment/request/otp | Authorization: Bearer <gateway> | Aadhaar: scope: ['abha-enrol'], loginHint: 'aadhaar', loginId: <encrypted_aadhaar>, otpSystem: 'aadhaar' |
| 2 | POST /abha/api/v3/enrollment/enrol/byAadhaar | Authorization: Bearer <gateway> | { authData: { authMethods: ['otp'], otp: { txnId, otpValue: <encrypted_otp>, mobile } }, consent: { code: 'abha-enrollment', version: '1.4' } } |
| 3 | POST /abha/api/v3/enrollment/auth/byAbdm | Authorization: Bearer <gateway> | { scope: ['abha-enrol', 'mobile-verify'], authData: { authMethods: ['otp'], otp: { txnId, otpValue: <encrypted_otp> } } } |
| 4 | POST /abha/api/v3/profile/account/request/emailVerificationLink | Authorization: Bearer <X-token> | (body per swagger) |
| 5 | GET /abha/api/v3/enrollment/enrol/suggestion | Authorization: Bearer <gateway> | Header Transaction_Id: <txnId>, REQUEST-ID, TIMESTAMP |
| 6 | POST /abha/api/v3/enrollment/enrol/abha-address | Authorization: Bearer <gateway> | { txnId, abhaAddress, preferred: 1 } |
| 7 | GET /abha/api/v3/profile/account | Authorization: Bearer <X-token> | Headers: X-token: Bearer <x_token>, REQUEST-ID, TIMESTAMP |
| 8 | GET /abha/api/v3/profile/account/abha-card | Authorization: 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 endpoint | Our implementation | Status |
|---|---|---|
/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/byAadhaar | Not implemented. | 🚧 Missing |
/enrollment/auth/byAbdm | Not implemented. | 🚧 Missing |
/profile/account/request/emailVerificationLink | Phr::Profile::Email::RequestVerificationLink | ✅ Exists |
/enrollment/enrol/suggestion | Phr::Registration::Enrolment::Suggestion.perform → ABDM /enrollment/suggestion | ⚠️ Wrong URL (spec says enrol/suggestion, we call enrollment/suggestion) |
/enrol/abha-address | Phr::Registration::Enrolment::Enrol.perform → ABDM /enrollment/enrol | ⚠️ Wrong URL (spec says enrol/abha-address, we call enrollment/enrol) |
/profile/account | Not implemented in PHR. /phr/patients/profile exists but uses different ABDM endpoint. | 🚧 Missing |
/profile/account/abha-card | Not implemented. fetch_phr_card_service.rb exists but unclear if it uses this endpoint. | 🚧 Missing |
FE coverage
| FE file | Endpoint | Status |
|---|---|---|
AbhaRegistrationForm.tsx | send_otp → verify_otp → suggest → check_address → enrol (mobile-OTP path) | ✅ |
LoginForm.tsx | abha_number_aadhaar/request_otp → abha_number_aadhaar/verify_otp (login mode, NOT creation) | ✅ (login only) |
AbhaRegistrationForm.tsx | No Aadhaar-based creation flow | ❌ Missing |
Verhoef algorithm / Aadhaar validation
Not implemented anywhere. Per spec:
- Last digit is checksum
verhoeff_algorithmvalidator 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 createdC. 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.pngM2 — 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
M3 — Consent Management + Data Flow
Status: testing in progress (see previous testing guide)
- 3 consents in
requestedstatus - 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