ABDM / NHA FHIR R4 NHCX Razorpay Twilio Health Connect SSO / SAML AI / LLM Outbound Webhooks

Integrations

PatientPulse is pre-wired to India's Digital Health stack and key SaaS providers. Each integration is toggled via environment variables — no custom code required for standard configurations.

ABDM — Ayushman Bharat Digital Mission

India's National Digital Health Infrastructure — ABHA identity, PHR push/pull, HFR/HPR registry sync

Native Support

PatientPulse ships an ABDM connector that handles the full integration lifecycle. In development, set ABDM_CONNECTOR_MODE=stub for mock responses. Flip to live for production.

What's covered

✓ ABHA identity linking ✓ PHR push on consult completion ✓ PHR pull (patient-consented) ✓ HFR (Health Facility Registry) sync ✓ HPR (Health Professional Registry) sync ✓ Mobile ABHA linking endpoint ✓ Auto-push FHIR bundle on consult (Feature 7A)

Environment Variables

ABDM_INTEGRATION_ENABLED=true
ABDM_CONNECTOR_MODE=stub | live
ABDM_CONNECTOR_BASE_URL=https://abdm-sandbox.pp.in
ABDM_CONNECTOR_API_KEY=your_abdm_key
ABDM_CONNECTOR_IDENTITY_LINK_PATH=/v1/link
ABDM_CONNECTOR_PHR_PULL_PATH=/v1/phr/pull
ABDM_AUTO_PUSH_ON_CONSULT=true

Setup Steps

1
Register on NHA Developer Portal — get sandbox credentials at sandbox.abdm.gov.in
2
Set env vars above in your .env file. Use stub mode for development.
3
Test ABHA linking via GET /interop/abdm/identity-links/{link_request_id}
4
Enable auto-push — set ABDM_AUTO_PUSH_ON_CONSULT=true to push FHIR bundles on every completed consult
🤖 Codex Prompt — Add ABDM Webhook Receiver
Codex Prompt
Add a new route file backend/app/routes/webhooks_abdm.py to PatientPulse
that handles incoming ABDM notifications:

- POST /webhooks/abdm/consent-artefact  — patient consented to data share
- POST /webhooks/abdm/health-data-push  — ABDM pushing health data to us
- POST /webhooks/abdm/link-confirm      — ABHA link confirmed by patient

Each handler should:
1. Validate X-ABDM-Signature header (HMAC-SHA256 with ABDM_WEBHOOK_SECRET env var)
2. Parse the payload into Pydantic models
3. Emit a Postgres LISTEN/NOTIFY event for downstream processing
4. Return HTTP 200 {"status": "received"}

Register in main_domains/ under the interop chunk.

FHIR R4 — HL7 Interoperability

HL7 FHIR R4 patient export, clinical bundles, and adapter contracts for third-party system integration

Certified

PatientPulse maps its internal domain models to HL7 FHIR R4 resources. Use the GET /interop/fhir/r4/patients/{patient_id} endpoint to export any patient as a standards-compliant Bundle.

Supported FHIR Resources

Bundle Patient Condition MedicationRequest Observation Appointment Encounter DiagnosticReport Practitioner Organization
cURL
curl -X GET https://api.patientpulse.in/interop/fhir/r4/patients/pat_abc123 \
  -H "Authorization: Bearer <your_token>" \
  -H "Accept: application/fhir+json"
🤖 Codex Prompt — Extend FHIR to include DiagnosticReport
Codex Prompt
In the PatientPulse FHIR adapter (find the file handling GET /interop/fhir/r4/patients/{id}),
extend the Bundle to include DiagnosticReport resources for each lab test result
associated with the patient.

Each DiagnosticReport should include:
- resourceType: "DiagnosticReport"
- status: from the PatientPulse lab result status
- code: LOINC code where available, fallback to local code
- subject: Reference to Patient
- result: array of Observation references (one per marker)
- issued: timestamp of the lab result

Map PatientPulse lab result fields to the FHIR schema.
Add unit tests for the new mapper function.

NHCX — National Health Claims Exchange

Submit insurance claims, poll real-time status, and handle pre-authorization via India's unified claims gateway

Live

PatientPulse integrates with NHCX for end-to-end insurance claim submission. Claims go through the workbench (GET /insurance/claims/{id}/workbench), are submitted via the connector, and status is polled using the correlation ID.

Claim Lifecycle

1
Pre-auth requestPOST /insurance/claims with claim type preauth
2
Payer rules checkGET /insurance/payer-rules returns applicable policy rules
3
Claims workbench — pack documents, review, and submit to NHCX
4
Status pollingGET /insurance/nhcx/claim-status/{correlation_id} for real-time status
5
Settlement — discounts and cashier settlement via IPD cashier endpoint

Razorpay — Payments & Subscriptions

Patient payments, clinic subscription billing, GST invoicing, refunds, and webhook verification

Live
RAZORPAY_KEY_ID=rzp_test_xxxxx
RAZORPAY_KEY_SECRET=your_secret
SUBSCRIPTION_STARTER_MONTHLY=999900
SUBSCRIPTION_GROWTH_MONTHLY=999900

Key Flows

POST /payments/orders — create payment order POST /payments/verify — HMAC verification POST /subscription/checkout — subscription flow POST /webhooks/razorpay — payment event handler GET /appointments/{id}/pay-link — share payment link GET /appointments/{id}/receipt — GST receipt

Developer API Plan Activation

When creating a Razorpay subscription for developer API access, pass the following notes fields so the webhook can identify the org and promote the plan automatically:

Razorpay notes
{
  "product": "developer_api",
  "org_id":  "<your PatientPulse org ID>",
  "plan":    "growth"   // or "enterprise"
}
How the webhook handles it

On subscription.activated / subscription.charged: PatientPulse upserts organization_subscriptions, promotes your plan tier, and auto-issues a pp_live_sk_… production key emailed to your billing_email — no manual key creation needed.

Twilio — SMS, WhatsApp & OTP

Patient OTP delivery, appointment reminders, and incoming WhatsApp message handling

Live
TWILIO_ACCOUNT_SID=ACxxxx
TWILIO_AUTH_TOKEN=your_token
TWILIO_VERIFY_SERVICE_SID=VAxxxx
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886

Twilio is also used in the PulseVault Node.js backend for the same OTP flow. Rate limits apply: configurable via AUTH_OTP_PHONE_LIMIT_PER_15_MIN.

Android Health Connect

Wearable and fitness data ingestion — steps, heart rate, sleep, and chronic condition markers

Android Only

PulseVault integrates with Android Health Connect to pull wearable data into a patient's profile. Data is synced to the family profile and surfaces in the AI Insights screen as trending markers.

Supported Data Types

Steps (daily/hourly) Heart Rate Sleep duration & stages Blood glucose Blood pressure Weight Oxygen saturation (SpO2)

PatientPulse backend receives Health Connect payloads via POST /health-connect/sync (Feature 4). The server stores them as structured markers linked to the patient's profile.

🤖 Codex Prompt — Add Health Connect Background Sync Worker
Codex Prompt
In the PulseVault Android app (apps/patient-android or the Vault repo),
add a background sync worker using WorkManager to periodically sync
Health Connect data to the PatientPulse backend.

Requirements:
- Create HealthConnectSyncWorker : CoroutineWorker in feature/vault or core/data
- Read the following Health Connect record types: StepsRecord, HeartRateRecord,
  SleepSessionRecord, BloodGlucoseRecord, BloodPressureRecord
- Transform into the payload expected by POST /v1/health-connect/sync
- Schedule with PeriodicWorkRequest: every 6 hours, requires network
- Handle permission denial gracefully — show HealthConnectPermissionBanner
  composable if permissions not granted
- Inject via Hilt; bind in the Application class

SAML 2.0 SSO & Google OAuth

Enterprise SSO for clinic staff login, and Google OAuth for patient self-service

Enterprise
SSO_BASE_URL=https://sso.yourhospital.in
SSO_SAML_SP_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----
GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxx

SAML 2.0 SSO uses python3-saml. Initiate via POST /auth/sso/start and discover providers at GET /auth/sso/discovery. Per-org SSO config is stored in organization settings.

AI / LLM — OpenAI & Ollama

Cloud LLM via OpenAI GPT-4, or on-premise via Ollama for privacy-sensitive deployments

Configurable
AI_ENABLED=true
AI_EXTERNAL_LLM_ENABLED=true
OPENAI_API_KEY=sk-xxxx
OPENAI_MODEL=gpt-4o
AI_LOCAL_MODEL_URL=http://localhost:11434
AI_LOCAL_MODEL_NAME=llama3.1
AI_NO_RAW_PATIENT_DATA=true
AI_RATE_LIMIT_PER_MINUTE=60
✓ Privacy-first AI

Set AI_NO_RAW_PATIENT_DATA=true to strip all identifiers before sending prompts to the LLM. Use AI_LOCAL_MODEL_URL with Ollama for fully on-premise deployments that never leave your network.

🤖 Codex Prompt — Migrate AI from OpenAI to Claude (Anthropic)
Codex Prompt
In the PatientPulse backend, find the AI service module (the file handling
POST /ai/interpret-lab-report and POST /ai/ambient-scribe).

Refactor the LLM client to support Anthropic Claude as an alternative to OpenAI:
- Add ANTHROPIC_API_KEY and ANTHROPIC_MODEL env vars (default: claude-sonnet-4-6)
- Add AI_PROVIDER env var: openai (default) | anthropic | local
- Create an AbstractLLMClient base class with a .complete(prompt: str) -> str method
- Implement OpenAIClient, AnthropicClient, and OllamaClient subclasses
- Add prompt caching headers for Anthropic (cache-control: ephemeral on system prompts)
  to reduce cost on repeated clinical prompts
- Wire via factory function get_llm_client() that reads AI_PROVIDER from env
- No changes to the route handlers — only the service layer changes

Outbound Webhooks — PatientPulse → Your Server

Configure an HTTPS endpoint in the Developer Portal and PatientPulse will POST real-time event payloads to it.

Growth+

Event Catalogue

Event Trigger Key Payload Fields
appointment.createdNew appointment bookedappointment_id, patient_id, doctor_id, slot_time
appointment.cancelledAppointment cancelled by patient or staffappointment_id, reason, cancelled_by
consult.completedDoctor marks consultation completevisit_id, patient_id, doctor_id, diagnosis_codes[]
claim.submittedInsurance claim submitted to payerclaim_id, payer_id, amount, nhcx_ref
claim.approvedPayer approves the claimclaim_id, approved_amount, settlement_date
patient.registeredNew patient record created in orgpatient_id, abha_linked, org_id
ai.interpretation.readyAsync AI lab interpretation completejob_id, patient_id, report_url, markers[]
fhir.bundle.pushedFHIR bundle pushed to ABDM health lockerbundle_id, patient_id, abha_address, resource_count

Payload Envelope

Every outbound webhook POST has this top-level shape:

JSON
{
  "id":         "evt_01JXYZ...",          // unique event ID (idempotency key)
  "event":      "appointment.created",
  "org_id":     42,
  "timestamp":  "2026-04-30T10:15:00Z",
  "api_version": "2.0",
  "data": {
    // event-specific fields
  }
}

Signature Verification

Each request includes an X-PP-Signature header — HMAC-SHA256 of the raw request body signed with your webhook secret. Always verify before processing:

Python
import hashlib, hmac

def verify_signature(body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)
⚠ Always return 200 quickly

Respond with HTTP 200 within 5 seconds. PatientPulse retries with exponential back-off (3 attempts) if it receives a non-2xx or times out. Use a queue internally if processing is slow.

Get API Keys → API Reference