Kulcare Docs
DeveloperAuthentication Documentation04 featuresAbha_phr

ABHA Address Creation Implementation Plan

Overview

This document provides a comprehensive, production-ready implementation plan for the ABHA Address creation flow, strictly following existing ABDM module conventions and patterns. The implementation follows a stateless pattern without database transaction storage, consistent with the current ABDM module approach.

Architecture Overview

The ABHA Address creation flow supports three enrollment methods:

  1. Mobile-based enrollment - For new ABHA creation using mobile OTP
  2. ABHA Number-based enrollment - For existing ABHA holders to create new address
  3. Aadhaar-based enrollment - For Aadhaar-linked ABHA creation

Key Implementation Pattern

Stateless Flow

  • No database transactions: Transaction IDs are passed between API calls via frontend
  • Direct service communication: Each API call directly communicates with ABDM services
  • Audit logging only: Only AbdmRequest entries are created for logging
  • Final storage: ABHA details stored in clinic_patients only after successful enrollment

Existing Database Fields

The clinic_patients table already has ABDM-related fields (from migration 20220629084152):

  • health_id - Stores the ABHA address
  • health_id_number - Stores the ABHA number

These fields will be updated only after successful enrollment completion.

Services Layer

1. Mobile Enrollment - Request OTP Service

# parts/abdm/services/abdm/abha/enrollment/mobile/request_otp.rb
module Abdm
  module Abha
    module Enrollment
      module Mobile
        class RequestOtp
          def self.perform(mobile, user_id)
            token = Abdm::FetchGatewayTokenV3.perform
            encrypted_mobile = Abdm::Abha::EncryptData.perform(mobile)
            
            url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/app/enrollment/request/otp"
            headers = {
              'Content-Type' => 'application/json',
              'Authorization' => "Bearer #{token}",
              'REQUEST-ID' => SecureRandom.uuid,
              'TIMESTAMP' => Time.now.utc.iso8601(3)
            }
            
            body = {
              scope: ['abha-address-enroll', 'mobile-verify'],
              loginHint: 'mobile-number',
              loginId: encrypted_mobile,
              otpSystem: 'abdm'
            }
            
            begin
              result = HTTParty.post(url, body: body.to_json, headers: headers)
            rescue StandardError => e
              error = e.to_s
            end
            
            # Log Request
            abdm_request = AbdmRequest.create!(
              url: url,
              payload: body,
              requested_by_id: user_id,
              requested_by_type: 'User'
            )
            
            # Prepare response
            if result.present? && result.success?
              code = result.code
              success = true
              response = result.parsed_response
            elsif error.present?
              success = false
              response = error
            else
              success = false
              response = result.parsed_response
              code = result.code
            end
            
            abdm_request.update!(
              response: response,
              status_code: code,
              success: success
            )
            
            result
          end
        end
      end
    end
  end
end

2. Mobile Enrollment - Verify OTP Service

# parts/abdm/services/abdm/abha/enrollment/mobile/verify_otp.rb
module Abdm
  module Abha
    module Enrollment
      module Mobile
        class VerifyOtp
          def self.perform(otp, txn_id, user_id)
            token = Abdm::FetchGatewayTokenV3.perform
            encrypted_otp = Abdm::Abha::EncryptData.perform(otp)
            
            url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/app/enrollment/verify"
            headers = {
              'Content-Type' => 'application/json',
              'Authorization' => "Bearer #{token}",
              'REQUEST-ID' => SecureRandom.uuid,
              'TIMESTAMP' => Time.now.utc.iso8601(3)
            }
            
            body = {
              scope: ['abha-address-enroll', 'mobile-verify'],
              authData: {
                authMethods: ['otp'],
                otp: {
                  txnId: txn_id,
                  otpValue: encrypted_otp
                }
              }
            }
            
            begin
              result = HTTParty.post(url, body: body.to_json, headers: headers)
            rescue StandardError => e
              error = e.to_s
            end
            
            # Log Request
            abdm_request = AbdmRequest.create!(
              url: url,
              payload: body,
              requested_by_id: user_id,
              requested_by_type: 'User'
            )
            
            # Process response
            if result.present? && result.success?
              code = result.code
              success = true
              response = result.parsed_response
            elsif error.present?
              success = false
              response = error
            else
              success = false
              response = result.parsed_response
              code = result.code
            end
            
            abdm_request.update!(
              response: response,
              status_code: code,
              success: success
            )
            
            result
          end
        end
      end
    end
  end
end

3. ABHA Number Enrollment - Request OTP Service

# parts/abdm/services/abdm/abha/enrollment/abha_number/request_otp.rb
module Abdm
  module Abha
    module Enrollment
      module AbhaNumber
        class RequestOtp
          def self.perform(abha_number, user_id)
            token = Abdm::FetchGatewayTokenV3.perform
            encrypted_abha = Abdm::Abha::EncryptData.perform(abha_number)
            
            url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/app/enrollment/request/otp"
            headers = {
              'Content-Type' => 'application/json',
              'Authorization' => "Bearer #{token}",
              'REQUEST-ID' => SecureRandom.uuid,
              'TIMESTAMP' => Time.now.utc.iso8601(3)
            }
            
            body = {
              scope: ['abha-login', 'mobile-verify'],
              loginHint: 'abha-number',
              loginId: encrypted_abha,
              otpSystem: 'abdm'
            }
            
            begin
              result = HTTParty.post(url, body: body.to_json, headers: headers)
            rescue StandardError => e
              error = e.to_s
            end
            
            # Log Request
            abdm_request = AbdmRequest.create!(
              url: url,
              payload: body,
              requested_by_id: user_id,
              requested_by_type: 'User'
            )
            
            # Process response
            if result.present? && result.success?
              code = result.code
              success = true
              response = result.parsed_response
            elsif error.present?
              success = false
              response = error
            else
              success = false
              response = result.parsed_response
              code = result.code
            end
            
            abdm_request.update!(
              response: response,
              status_code: code,
              success: success
            )
            
            result
          end
        end
      end
    end
  end
end

4. Suggest ABHA Address Service

# parts/abdm/services/abdm/abha/enrollment/suggest_abha_address.rb
module Abdm
  module Abha
    module Enrollment
      class SuggestAbhaAddress
        def self.perform(txn_id, first_name, middle_name, last_name, year_of_birth, user_id)
          token = Abdm::FetchGatewayTokenV3.perform
          
          url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/app/enrollment/suggestion"
          headers = {
            'Content-Type' => 'application/json',
            'Authorization' => "Bearer #{token}",
            'REQUEST-ID' => SecureRandom.uuid,
            'TIMESTAMP' => Time.now.utc.iso8601(3)
          }
          
          body = {
            txnId: txn_id,
            firstName: first_name,
            middleName: middle_name,
            lastName: last_name,
            yearOfBirth: year_of_birth
          }
          
          begin
            result = HTTParty.post(url, body: body.to_json, headers: headers)
          rescue StandardError => e
            error = e.to_s
          end
          
          # Log Request
          abdm_request = AbdmRequest.create!(
            url: url,
            payload: body,
            requested_by_id: user_id,
            requested_by_type: 'User'
          )
          
          # Process response
          if result.present? && result.success?
            response = result.parsed_response
            abdm_request.update!(
              response: response,
              status_code: result.code,
              success: true
            )
            { success: true, data: response }
          else
            error_msg = error || result&.parsed_response
            abdm_request.update!(
              response: error_msg,
              status_code: result&.code,
              success: false
            )
            { success: false, error: error_msg }
          end
        end
      end
    end
  end
end

5. Check ABHA Address Existence Service

# parts/abdm/services/abdm/abha/enrollment/check_exists.rb
module Abdm
  module Abha
    module Enrollment
      class CheckExists
        def self.perform(abha_address, user_id)
          token = Abdm::FetchGatewayTokenV3.perform
          
          url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/app/enrollment/isExists"
          headers = {
            'Content-Type' => 'application/json',
            'Authorization' => "Bearer #{token}",
            'REQUEST-ID' => SecureRandom.uuid,
            'TIMESTAMP' => Time.now.utc.iso8601(3)
          }
          
          params = { abhaAddress: abha_address }
          
          begin
            result = HTTParty.get(url, headers: headers, query: params)
          rescue StandardError => e
            error = e.to_s
          end
          
          # Log Request
          abdm_request = AbdmRequest.create!(
            url: "#{url}?#{params.to_query}",
            payload: params,
            requested_by_id: user_id,
            requested_by_type: 'User'
          )
          
          # Process response
          if result.present? && result.success?
            response = result.parsed_response
            abdm_request.update!(
              response: response,
              status_code: result.code,
              success: true
            )
            { success: true, data: response }
          else
            error_msg = error || result&.parsed_response
            abdm_request.update!(
              response: error_msg,
              status_code: result&.code,
              success: false
            )
            { success: false, error: error_msg }
          end
        end
      end
    end
  end
end

6. Complete Enrollment Service

# parts/abdm/services/abdm/abha/enrollment/enroll.rb
module Abdm
  module Abha
    module Enrollment
      class Enroll
        def self.perform(txn_id, phr_details, user_id)
          token = Abdm::FetchGatewayTokenV3.perform
          
          # Encrypt sensitive fields
          encrypted_details = encrypt_phr_details(phr_details)
          
          url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/app/enrollment/enrol"
          headers = {
            'Content-Type' => 'application/json',
            'Authorization' => "Bearer #{token}",
            'REQUEST-ID' => SecureRandom.uuid,
            'TIMESTAMP' => Time.now.utc.iso8601(3)
          }
          
          body = {
            txnId: txn_id,
            phrDetails: encrypted_details
          }
          
          begin
            result = HTTParty.post(url, body: body.to_json, headers: headers)
          rescue StandardError => e
            error = e.to_s
          end
          
          # Log Request (excluding sensitive data)
          abdm_request = AbdmRequest.create!(
            url: url,
            payload: { txnId: txn_id, phrDetails: 'ENCRYPTED' },
            requested_by_id: user_id,
            requested_by_type: 'User'
          )
          
          # Process response
          if result.present? && result.success?
            response = result.parsed_response
            abdm_request.update!(
              response: response,
              status_code: result.code,
              success: true
            )
            { success: true, data: response }
          else
            error_msg = error || result&.parsed_response
            abdm_request.update!(
              response: error_msg,
              status_code: result&.code,
              success: false
            )
            { success: false, error: error_msg }
          end
        end
        
        private
        
        def self.encrypt_phr_details(details)
          encrypted_details = details.dup
          
          # Encrypt sensitive fields
          %w[mobile email password].each do |field|
            if encrypted_details[field].present?
              encrypted_details[field] = Abdm::Abha::EncryptData.perform(details[field])
            end
          end
          
          encrypted_details
        end
      end
    end
  end
end

Interactors (Business Logic)

1. Mobile Enrollment - Send OTP Interactor

# parts/abdm/interactors/abdm/abha/enrollment/mobile/send_otp.rb
module Abdm
  module Abha
    module Enrollment
      module Mobile
        class SendOtp < BaseInteractor
          def call
            require_context(:mobile)
            
            result = Services::Mobile::RequestOtp.perform(
              context.mobile,
              context.current_user&.id
            )
            
            fail_with_errors!(result['errors']) if result.key?('errors')
            
            context.records = result
            context.message = 'OTP sent successfully'
          end
        end
      end
    end
  end
end

2. Mobile Enrollment - Verify OTP Interactor

# parts/abdm/interactors/abdm/abha/enrollment/mobile/verify_otp.rb
module Abdm
  module Abha
    module Enrollment
      module Mobile
        class VerifyOtp < BaseInteractor
          def call
            require_context(:otp)
            require_context(:txn_id)
            
            result = Services::Mobile::VerifyOtp.perform(
              context.otp,
              context.txn_id,
              context.current_user&.id
            )
            
            fail_with_errors!(result['errors']) if result.key?('errors')
            
            context.records = result
            context.message = 'OTP verified successfully'
          end
        end
      end
    end
  end
end

3. Suggest ABHA Address Interactor

# parts/abdm/interactors/abdm/abha/enrollment/suggest_address.rb
module Abdm
  module Abha
    module Enrollment
      class SuggestAddress < BaseInteractor
        def call
          require_context(:txn_id)
          require_context(:firstName)
          require_context(:yearOfBirth)
          
          result = Services::SuggestAbhaAddress.perform(
            context.txn_id,
            context.firstName,
            context.middleName,
            context.lastName,
            context.dayOfBirth,
            context.monthOfBirth,
            context.yearOfBirth,
            context.email,
            context.current_user&.id
          )
          
          fail_with_errors!(result['errors']) if result.key?('errors')
          
          context.records = result
          context.message = 'ABHA address suggestions retrieved'
        end
      end
    end
  end
end

4. Check ABHA Address Availability Interactor

# parts/abdm/interactors/abdm/abha/enrollment/check_availability.rb
module Abdm
  module Abha
    module Enrollment
      class CheckAvailability < BaseInteractor
        def call
          require_context(:abhaAddress)
          
          result = Services::CheckExists.perform(
            context.abhaAddress,
            context.current_user&.id
          )
          
          fail_with_errors!(result['errors']) if result.key?('errors')
          
          context.records = result
          context.message = result['status'] ? 'ABHA address already exists' : 'ABHA address is available'
        end
      end
    end
  end
end

5. Complete Enrollment Interactor

# parts/abdm/interactors/abdm/abha/enrollment/create_enrollment.rb
module Abdm
  module Abha
    module Enrollment
      class CreateEnrollment < BaseInteractor
        def call
          require_context(:txn_id)
          require_context(:phrDetails)
          
          result = Services::Enroll.perform(
            context.txn_id,
            context.phrDetails,
            context.current_user&.id
          )
          
          fail_with_errors!(result['errors']) if result.key?('errors')
          
          # Update clinic patient if provided
          update_clinic_patient(result) if context.clinic_patient_id.present?
          
          context.records = result
          context.message = 'ABHA address created successfully'
        end
        
        private
        
        def update_clinic_patient(response_data)
          clinic_patient = ClinicPatient.find_by(id: context.clinic_patient_id)
          return unless clinic_patient
          
          clinic_patient.update!(
            health_id: response_data['phrAddress'] || response_data['abhaAddress'],
            health_id_number: response_data['ABHANumber'] || response_data['abhaNumber']
          )
        end
      end
    end
  end
end

Controllers

ABHA Address Enrollments Controller

# parts/abdm/controllers/abdm/abha/abha_address_enrollments_controller.rb
module Abdm
  module Abha
    class AbhaAddressEnrollmentsController < ApiController
      before_action :policy_check
      
      # Mobile enrollment endpoints
      
      # POST /abdm/abha/abha_address_enrollments/mobile/send_otp
      def mobile_send_otp
        result = Enrollment::Mobile::SendOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/mobile/verify_otp
      def mobile_verify_otp
        result = Enrollment::Mobile::VerifyOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/mobile/resend_otp
      def mobile_resend_otp
        result = Enrollment::Mobile::ResendOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # ABHA Number enrollment endpoints
      
      # POST /abdm/abha/abha_address_enrollments/abha_number/send_otp
      def abha_number_send_otp
        result = Enrollment::AbhaNumber::SendOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/abha_number/verify_otp
      def abha_number_verify_otp
        result = Enrollment::AbhaNumber::VerifyOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/abha_number/resend_otp
      def abha_number_resend_otp
        result = Enrollment::AbhaNumber::ResendOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # Aadhaar enrollment endpoints
      
      # POST /abdm/abha/abha_address_enrollments/aadhaar/send_otp
      def aadhaar_send_otp
        result = Enrollment::Aadhaar::SendOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/aadhaar/verify_otp
      def aadhaar_verify_otp
        result = Enrollment::Aadhaar::VerifyOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/aadhaar/resend_otp
      def aadhaar_resend_otp
        result = Enrollment::Aadhaar::ResendOtp.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # Common enrollment endpoints
      
      # POST /abdm/abha/abha_address_enrollments/suggest_address
      def suggest_address
        result = Enrollment::SuggestAddress.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # GET /abdm/abha/abha_address_enrollments/check_availability
      def check_availability
        result = Enrollment::CheckAvailability.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      # POST /abdm/abha/abha_address_enrollments/complete_enrollment
      def complete_enrollment
        result = Enrollment::CreateEnrollment.call(params)
        render_result(result, result.success? ? AbdmAbhaSerializer : nil)
      end
      
      private
      
      def policy_check
        authorize params, policy_class: AbhaAddressEnrollmentsControllerPolicy
      end
    end
  end
end

Routes Configuration

# parts/abdm/routes.rb (Addition to existing routes in the ABHA namespace)
# Add this inside the existing 'namespace :abha do' block

resources :abha_address_enrollments, only: [] do
  collection do
    # Mobile enrollment
    namespace :mobile do
      post :send_otp
      post :verify_otp
      post :resend_otp
    end
    
    # ABHA Number enrollment
    namespace :abha_number do
      post :send_otp
      post :verify_otp
      post :resend_otp
    end
    
    # Aadhaar enrollment
    namespace :aadhaar do
      post :send_otp
      post :verify_otp
      post :resend_otp
    end
    
    # Common enrollment endpoints
    post :suggest_address
    get :check_availability
    post :complete_enrollment
  end
end

Authorization Policy

# parts/abdm/policies/abdm/abha/abha_address_enrollments_controller_policy.rb
module Abdm
  module Abha
    class AbhaAddressEnrollmentsControllerPolicy < BasePolicy
      def mobile_send_otp?
        user_signed_in?
      end
      
      def mobile_verify_otp?
        user_signed_in?
      end
      
      def mobile_resend_otp?
        user_signed_in?
      end
      
      def abha_number_send_otp?
        user_signed_in?
      end
      
      def abha_number_verify_otp?
        user_signed_in?
      end
      
      def abha_number_resend_otp?
        user_signed_in?
      end
      
      def aadhaar_send_otp?
        user_signed_in?
      end
      
      def aadhaar_verify_otp?
        user_signed_in?
      end
      
      def aadhaar_resend_otp?
        user_signed_in?
      end
      
      def suggest_address?
        user_signed_in?
      end
      
      def check_availability?
        user_signed_in?
      end
      
      def complete_enrollment?
        user_signed_in?
      end
    end
  end
end

Serialization

The existing AbdmAbhaSerializer will be used for all enrollment API responses. This serializer passes through the ABDM response data directly:

# Existing serializer: parts/abdm/serializers/abdm_abha_serializer.rb
class AbdmAbhaSerializer < BaseSerializer
  def attributes(data, type = nil)
    data = super
    data[:data] = object[:data]
    data
  end
end

API Request/Response Examples

1. Mobile Enrollment - Send OTP

Request:

POST /abdm/abha/abha_address_enrollments/mobile/send_otp
{
  "mobile": "9876543210"
}

Response:

{
  "success": true,
  "message": "OTP sent successfully",
  "data": {
    "txnId": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
    "message": "OTP has been sent to your mobile number"
  }
}

2. Mobile Enrollment - Verify OTP

Request:

POST /abdm/abha/abha_address_enrollments/mobile/verify_otp
{
  "otp": "123456",
  "txn_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
}

Response:

{
  "success": true,
  "message": "OTP verified successfully",
  "data": {
    "txnId": "updated-transaction-id",
    "mobileLinked": true
  }
}

3. Suggest ABHA Address

Request:

POST /abdm/abha/abha_address_enrollments/suggest_address
{
  "txn_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
  "firstName": "John",
  "middleName": "",
  "lastName": "Doe",
  "dayOfBirth": "15",
  "monthOfBirth": "06",
  "yearOfBirth": "1990",
  "email": "john.doe@example.com"
}

Response:

{
  "success": true,
  "message": "ABHA address suggestions retrieved",
  "data": {
    "txnId": "updated-transaction-id",
    "abhaAddressList": [
      "john.doe",
      "johndoe90",
      "john_1990"
    ]
  }
}

4. Check ABHA Address Availability

Request:

GET /abdm/abha/abha_address_enrollments/check_availability?abhaAddress=johndoe@abdm

Response:

{
  "success": true,
  "message": "ABHA address is available",
  "data": {
    "status": false
  }
}

5. Complete Enrollment

Request:

POST /abdm/abha/abha_address_enrollments/complete_enrollment
{
  "txn_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
  "phrDetails": {
    "mobile": "encrypted_mobile_value",
    "firstName": "John",
    "middleName": "",
    "lastName": "Doe",
    "yearOfBirth": "1990",
    "dayOfBirth": "15",
    "monthOfBirth": "06",
    "gender": "M",
    "email": "encrypted_email_value",
    "profilePhoto": "",
    "address": "123 Main Street",
    "stateName": "Maharashtra",
    "stateCode": "27",
    "districtName": "Mumbai",
    "districtCode": "519",
    "pinCode": "400001",
    "abhaAddress": "johndoe@abdm",
    "password": "encrypted_password_value"
  },
  "clinic_patient_id": 123
}

Response:

{
  "success": true,
  "message": "ABHA address created successfully",
  "data": {
    "phrAddress": "johndoe@abdm",
    "abhaNumber": "91-1234-5678-9012",
    "tokens": {
      "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
      "expiresIn": 1800
    },
    "refreshTokenExpiresIn": 432000
  }
}

Error Handling

All API endpoints follow the existing ABDM error handling pattern:

# Standard error response format
{
  "success": false,
  "error": {
    "code": "ABDM-001",
    "message": "Invalid OTP",
    "details": {
      "field": "otp",
      "reason": "OTP has expired or is invalid"
    }
  }
}

Testing Strategy

1. Unit Tests

# spec/services/abdm/abha/enrollment/mobile/request_otp_spec.rb
# spec/interactors/abdm/abha/enrollment/mobile/send_otp_spec.rb
# spec/controllers/abdm/abha/enrollments_controller_spec.rb

2. Integration Tests

# spec/requests/abdm/abha/enrollment_flow_spec.rb

3. Factory Setup

# spec/factories/abha_enrollment_transactions.rb
FactoryBot.define do
  factory :abha_enrollment_transaction do
    clinic_patient
    transaction_id { SecureRandom.uuid }
    enrollment_type { 'mobile' }
    status { 'initiated' }
    expires_at { 10.minutes.from_now }
  end
end

Configuration Requirements

Add to Rails credentials:

abdm_phr_host: "https://abhasbx.abdm.gov.in/abha/api"
abdm_hid_host_v3: "https://abhasbx.abdm.gov.in/abha/api"
abdm_cm_id: "sbx"

Implementation Steps

  1. Phase 1: Service Layer

    • Implement all service classes for ABDM API communication
    • Follow existing patterns from current ABDM services
    • Test with ABDM sandbox environment
  2. Phase 2: Business Logic

    • Create lightweight interactors following existing patterns
    • Add input validation and error handling
    • No database transaction management required
  3. Phase 3: API Layer

    • Implement controller with all enrollment endpoints
    • Configure routes in existing ABDM routes file
    • Create authorization policy
  4. Phase 4: Integration & Testing

    • Integration testing with ABDM sandbox
    • Error scenario testing
    • Update clinic_patient fields after successful enrollment

Security Considerations

  1. All sensitive data (mobile, email, passwords) encrypted using RSA-OAEP
  2. Transaction IDs managed by ABDM, expire after their defined timeout
  3. All API requests logged via AbdmRequest for audit trail
  4. PII data excluded from logs
  5. Authentication required for all endpoints via user_signed_in?

Monitoring & Observability

  1. Track enrollment success/failure rates via AbdmRequest logs
  2. Monitor ABDM API response times
  3. Alert on repeated API failures
  4. Dashboard for enrollment analytics

This implementation strictly follows the existing ABDM module patterns and conventions, ensuring consistency and maintainability.

On this page