PHR Scan & Share — Curl Test Plan
Companion to: PHR Scan & Share API Reference Backend port: 6000 Last verified: 2026-07-23
This is a reproducible end-to-end test of the Scan & Share flow using curl.
Prerequisites
export ABHA_TOKEN="<from login>"
export USER_ID="<from kulcare_session.user.id>"
export HIP_ID="IN1810000370"
export COUNTER_ID="01900006-0000-7000-8000-000000000002"
export BASE="http://localhost:6000"The ABHA_TOKEN is from the /phr/login/abha_address_aadhaar/verify_otp response:
{ "data": { "abha_token": "eyJ...", "kulcare_session": { "user": { "id": "uuid" } } } }Test 1: Successful share end-to-end
Step 1 — Initiate share
curl -s -X POST "$BASE/phr/patients/share_profile" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ABHA_TOKEN" \
-H "REQUEST-ID: $(uuidgen)" \
-H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
-d "{
\"token\": \"$ABHA_TOKEN\",
\"hip_id\": \"$HIP_ID\",
\"counter_id\": \"$COUNTER_ID\",
\"abha_address\": \"simmi1508@sbx\",
\"patient\": {
\"healthId\": \"simmi1508@sbx\",
\"healthIdNumber\": \"46-0315-7614-5529\",
\"name\": \"Simmi\",
\"gender\": \"F\",
\"dateOfBirth\": \"1986-08-15\",
\"mobile\": \"9815707711\"
}
}" | tee /tmp/share_response.jsonExpected response:
{
"success": true,
"data": {
"request_id": "<uuid>",
"status": "pending",
"abha_address": "simmi1508@sbx",
"hip_id": "IN1810000370",
"counter_id": "01900006-0000-7000-8000-000000000002",
"expires_in": 30
}
}Extract request_id:
export REQUEST_ID=$(cat /tmp/share_response.json | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['request_id'])")
echo "REQUEST_ID=$REQUEST_ID"Step 2 — Verify the share row was created in DB
cd /Users/simmibadhan/kulcare-platform/backend
bundle exec rails runner "
share = Phr::ProfileShare.find_by!(request_id: '$REQUEST_ID')
puts 'Status: ' + share.status
puts 'ABHA: ' + share.abha_address
puts 'HIP: ' + share.hip_id
puts 'Counter:' + share.counter_id
puts 'User: ' + share.user_id.to_s
puts 'Created:' + share.created_at.to_s
" 2>&1 | grep -v "warning:\|deprecat"Expected:
Status: pending
ABHA: simmi1508@sbx
HIP: IN1810000370
Counter:01900006-0000-7000-8000-000000000002
User: <user-uuid>
Created: 2026-07-23 ...Step 3 — Simulate ABDM's success callback
curl -s -X POST "$BASE/phr/share/on_share" \
-H 'Content-Type: application/json' \
-H "REQUEST-ID: $(uuidgen)" \
-H "TIMESTAMP: $(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
-d "{
\"acknowledgement\": {
\"abhaAddress\": \"simmi1508@sbx\",
\"status\": \"success\",
\"context\": \"$COUNTER_ID\",
\"tokenNumber\": \"3\",
\"expiry\": 180
},
\"response\": { \"requestId\": \"$REQUEST_ID\" }
}"Expected response: {"success": true}
Step 4 — Fetch status (RESTful)
curl -s "$BASE/phr/patients/share_profile/$REQUEST_ID" | python3 -m json.toolExpected:
{
"success": true,
"data": {
"request_id": "<uuid>",
"status": "success",
"abha_address": "simmi1508@sbx",
"hip_id": "IN1810000370",
"counter_id": "01900006-0000-7000-8000-000000000002",
"token_number": "3",
"expiry": 180,
"created_at": "2026-07-23T...",
"callback_received_at":"2026-07-23T..."
}
}Step 5 — Verify the share was updated in DB
cd /Users/simmibadhan/kulcare-platform/backend
bundle exec rails runner "
share = Phr::ProfileShare.find_by!(request_id: '$REQUEST_ID')
puts 'Status: ' + share.status
puts 'Token Number: ' + share.token_number.to_s
puts 'Expiry: ' + share.expiry.to_s
puts 'Callback received:' + share.callback_received_at.to_s
" 2>&1 | grep -v "warning:\|deprecat"Expected: status: success, token_number: 3, expiry: 180, callback_received_at populated.
Test 2: Failed share (ABDM returns error)
Step 1 — Initiate a fresh share
curl -s -X POST "$BASE/phr/patients/share_profile" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ABHA_TOKEN" \
-d "{
\"token\": \"$ABHA_TOKEN\",
\"hip_id\": \"$HIP_ID\",
\"counter_id\": \"$COUNTER_ID\",
\"abha_address\": \"simmi1508@sbx\"
}" > /tmp/share2.json
export REQUEST_ID2=$(cat /tmp/share2.json | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['request_id'])")Step 2 — Simulate ABDM's error callback
curl -s -X POST "$BASE/phr/share/on_share" \
-H 'Content-Type: application/json' \
-d "{
\"error\": { \"code\": \"ABDM-9999\", \"message\": \"Facility offline\" },
\"response\": { \"requestId\": \"$REQUEST_ID2\" }
}"Step 3 — Fetch status
curl -s "$BASE/phr/patients/share_profile/$REQUEST_ID2" | python3 -m json.toolExpected:
{
"success": true,
"data": {
"request_id": "<uuid>",
"status": "failed",
"error_code": "ABDM-9999",
"error_message": "Facility offline",
"callback_received_at": "2026-07-23T..."
}
}Test 3: Unknown requestId (ABDM retries with bad ID)
curl -s -X POST "$BASE/phr/share/on_share" \
-H 'Content-Type: application/json' \
-d "{
\"acknowledgement\": { \"status\": \"success\" },
\"response\": { \"requestId\": \"$(uuidgen)\" }
}"Expected: 200 {"success": true} — we return 200 so ABDM doesn't retry.
Test 4: Missing requestId (400)
curl -s -i -X POST "$BASE/phr/share/on_share" \
-H 'Content-Type: application/json' \
-d '{"acknowledgement": {"status": "success"}}'Expected: 400 Bad Request with {"success": false, "error": "requestId is required"}
Test 5: TTL sweeper (expired shares)
Step 1 — Create a stale share (created 40 minutes ago)
cd /Users/simmibadhan/kulcare-platform/backend
bundle exec rails runner "
Phr::ProfileShare.create!(
tenant: Identity::Tenant.first,
user_id: Identity::User.first.id,
request_id: SecureRandom.uuid,
hip_id: 'IN1810000370',
counter_id: 'TEST1',
abha_address: 'stale@sbx',
status: 'pending',
created_at: 40.minutes.ago,
updated_at: 40.minutes.ago
)
puts 'Created stale share'
" 2>&1 | grep -v "warning:\|deprecat"Step 2 — Run the sweeper
bundle exec rails runner 'Phr::ExpireStaleProfileSharesJob.perform_now' 2>&1 | grep -v "warning:\|deprecat"Expected: logs the number of expired shares.
Step 3 — Verify the share is now expired
bundle exec rails runner "
stale = Phr::ProfileShare.where(abha_address: 'stale@sbx').last
puts 'Status: ' + stale.status
puts 'Error code: ' + stale.error_code.to_s
puts 'Error message: ' + stale.error_message.to_s
" 2>&1 | grep -v "warning:\|deprecat"Expected:
Status: expired
Error code: CALLBACK_TIMEOUT
Error message: No ABDM on-share callback received within 1800.0 secondsTest 6: Ably push (real-time notification)
This test requires:
- The FE to be subscribed to channel
private:user:{user_id}with the event namesabdm.share_profile.completedandabdm.share_profile.failed - An Ably API key configured (
Rails.application.credentials.ably_api_key)
Step 1 — Check Ably is configured
cd /Users/simmibadhan/kulcare-platform/backend
bundle exec rails runner '
puts "Ably enabled: #{Notifications::Realtime.enabled?}"
puts "Key present: #{Rails.application.credentials.ably_api_key.present?}"
' 2>&1 | grep -v "warning:\|deprecat"Expected: Ably enabled: true.
Step 2 — Simulate the Ably publish manually (if FE is not available)
cd /Users/simmibadhan/kulcare-platform/backend
bundle exec rails runner "
share = Phr::ProfileShare.find_by!(request_id: '$REQUEST_ID')
if share.user_id.present?
Phr::RealtimePublisher.publish_completed(
user_id: share.user_id,
request_id: share.request_id
)
puts 'Published completed event'
else
puts 'Share has no user_id, cannot publish'
end
" 2>&1 | grep -v "warning:\|deprecat"The FE (or Ably debug console) should receive the event on the private:user:{user_id} channel with event name abdm.share_profile.completed.
Test 7: Missing token (400)
curl -s -i -X POST "$BASE/phr/patients/share_profile" \
-H 'Content-Type: application/json' \
-d "{
\"user_id\": \"$USER_ID\",
\"hip_id\": \"$HIP_ID\",
\"counter_id\": \"$COUNTER_ID\"
}"Expected: 400 / 422 with {"success": false, "error": "Patient session token (token) is required"}
Test 8: Status for unknown requestId (404)
curl -s -i "$BASE/phr/patients/share_profile/$(uuidgen)"Expected: 404 with {"success": false, "error": "Share not found"}
Cleanup
cd /Users/simmibadhan/kulcare-platform/backend
bundle exec rails runner '
Phr::ProfileShare.where(abha_address: "simmi1508@sbx").destroy_all
Phr::ProfileShare.where(abha_address: "stale@sbx").destroy_all
puts "Cleaned up"
' 2>&1 | grep -v "warning:\|deprecat"