Aadhaar Secure QR

1 endpoint · Core API

What is it?

The Aadhaar Secure QR API parses the UIDAI-issued QR code printed on every Aadhaar letter / e-Aadhaar PDF and returns the demographic fields (name, DOB, gender, address, masked mobile, masked email, photo) as structured JSON. The QR carries a UIDAI RSA-2048 digital signature — so we can also prove the data was actually issued by UIDAI and has not been tampered with.

No Aadhaar number is ever exposed — the QR only contains the last 4 digits.


What can I do with this product?

  • Reception / help-desk demographic capture — staff scans the printed Aadhaar, the form auto-fills.
  • Address auto-fill on signup / onboarding forms when the user voluntarily uploads their own Aadhaar.
  • Tamper detection — verify a scanned or photographed Aadhaar wasn't edited (signature check).
  • Bulk back-office workflows — re-process pre-uploaded Aadhaar copies offline, no consent UI needed.
  • Offline / air-gapped verification — pure parsing, no network call to UIDAI at request time.

How is this different from OVSE and DigiLocker?

All three return demographics, but they solve different problems:

Aadhaar Secure QRAadhaar OVSEDigiLocker
Source dataThe QR printed on the Aadhaar letterUIDAI Aadhaar App (live)UIDAI central DB (live)
End-user frictionZero — just upload an imageAadhaar App scan + consentDigiLocker login + consent
Photo qualityThumbnail (~1 KB compressed)Higher resolutionOriginal resolution
Proves UIDAI-issued✅ RSA-2048 signature✅ SD-JWT✅ XML signature
Proves live consent❌✅✅
Proves holder is present❌✅ Face auth✅ Login + OTP
Replay risk (someone else's QR)⚠️ Yes❌❌
Fits regulated KYC (RBI / SEBI / IRDAI)❌✅✅

Use Secure QR when you need fast, low-friction extraction of demographics with tamper-detection — and you accept that the data is whatever was on the printed Aadhaar at issuance time.

Use OVSE or DigiLocker when you need to prove the holder is currently consenting and physically present — account opening, loan origination, anything regulated.


Request modes

You can submit the QR in either of two ways:

  1. Image upload — Base64-encoded PNG/JPEG of the Aadhaar QR. We decode the QR for you.
  2. Decimal payload — the raw decimal string that some QR scanners return directly. Skips image decode (faster).

Photo quality

The photo embedded in the QR is a heavily compressed JPEG2000 thumbnail (~700-1000 bytes of source data) decoded server-side to PNG. This is by UIDAI's design — the entire QR has to fit on a printed Aadhaar letter. The photo is suitable for visual help-desk identification, NOT face-recognition or biometric matching. For a high-resolution photo, use the Aadhaar OVSE or DigiLocker API.


Benefits

  • Zero end-user friction — no app install, no OTP, no consent screen
  • Works offline — no live UIDAI dependency at request time
  • Tamper detection — optional RSA signature verification catches edited QRs
  • No Aadhaar number exposed — only last 4 digits are recoverable
  • Single API call — one POST, one JSON response with everything
  • Drop-in for form auto-fill — perfect for reception desks, onboarding flows, and bulk back-office processing

Note on signature verification

When you pass verify_signature: true, we attempt to verify the UIDAI RSA-2048 signature using every UIDAI public certificate we have on file. UIDAI periodically rotates their signing certificate (2017 → 2020 → 2023 → …) and does not always publish the new public key at predictable URLs. If the QR was signed by a cert we don't yet have, verified returns false — not because the QR is forged, but because we cannot prove authenticity without UIDAI's current public key. Treat verified: true as a strong positive signal; treat verified: false as inconclusive (could be tampered, could just be a cert-rotation gap). The demographic fields and photo are still extracted in either case.

POST

/api/v1/aadhaar/secure-qr/read

#

Parse a UIDAI Aadhaar Secure QR.

docs.signcare.io/core/api/aadhaar/aadhaar-secure-qr/#post-api-v1-aadhaar-secure-qr-read

Returns demographic fields (name, DOB, gender, address), masked mobile, and a server-decoded PNG photo. Optionally verifies the UIDAI RSA-2048 signature (set verify_signature to true).

Request parameters:data (base64 QR image) or payload (decimal string) is required; send exactly one. include_photo (default true) and verify_signature (default false) are optional. data_content_type and reference_id are accepted but not used. No Aadhaar number is exposed — the QR only carries the last 4 digits. Photo is a UIDAI-compressed thumbnail suitable for visual identification, NOT biometric matching. For higher-resolution photos use the Aadhaar OVSE or DigiLocker APIs.

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.
  • Content-Type
    stringrequired
    application/json

Body parameters

application/json
  • consent
    stringrequired
    Required. The end user's consent to this verification. Send `Y`.
    minLength 1default Y
  • consent_text
    stringrequired
    Required. The consent statement shown to the end user. Send the default text from the sample.
    minLength 1default I hear by declare my consent agreeme…
  • request_id
    stringoptional
    Optional. Your own id to correlate this call in your logs. A unique id is generated if you leave it out.
    nullable
  • data
    stringconditional
    Conditional: required when payload is not sent. Base64-encoded PNG or JPEG image of the Aadhaar Secure QR. Send either data or payload, not both.
    nullable
  • payload
    stringconditional
    Conditional: required when data is not sent. Raw decimal payload string scanned out of the QR by a QR reader. Send either data or payload, not both. Skips image decoding, so it is faster.
    nullable
  • data_content_type
    stringoptional
    Optional. Image format of data: PNG or JPEG. Accepted but not used; the format is detected from the image.
    nullabledefault PNG
  • include_photo
    booleanoptional
    Optional. Defaults to true, which returns the photo decoded to PNG as base64. Set false to skip photo decoding and return null in `photo` for a faster, smaller response.
    default true
  • verify_signature
    booleanoptional
    Optional. Defaults to false. Set true to verify the UIDAI RSA-2048 signature on the QR payload.
    default false
  • reference_id
    stringoptional
    Optional. Your own reference for this request. Accepted but not used or returned; the reference_id in the response comes from the QR itself.
    nullable
Sample request body
{ "consent": "Y", "consent_text": "I hear by declare my consent agreement for fetching my information via SignCare API", "data": "", "payload": "", "data_content_type": "PNG", "include_photo": true, "verify_signature": false, "reference_id": "" }

Responses

200OK
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Response model for the Aadhaar Secure QR parser.
    5 fields
    • qr_format
      stringoptional
      Always "secure_qr" in v1 — placeholder for forward compatibility.
      nullable
    • secure_qr_version
      stringoptional
      UIDAI Secure QR version (e.g. "V5"). Null when the QR omits the version field (older issuances).
      nullable
    • fields
      objectoptional
      Demographic fields parsed from the QR text block.
      20 fields
      • name
        stringoptional
        No description.
        nullable
      • dob
        stringoptional
        No description.
        nullable
      • gender
        stringoptional
        No description.
        nullable
      • care_of
        stringoptional
        No description.
        nullable
      • house
        stringoptional
        No description.
        nullable
      • street
        stringoptional
        No description.
        nullable
      • landmark
        stringoptional
        No description.
        nullable
      • location
        stringoptional
        No description.
        nullable
      • vtc
        stringoptional
        No description.
        nullable
      • sub_district
        stringoptional
        No description.
        nullable
      • district
        stringoptional
        No description.
        nullable
      • state
        stringoptional
        No description.
        nullable
      • post_office
        stringoptional
        No description.
        nullable
      • pincode
        stringoptional
        No description.
        nullable
      • masked_mobile
        stringoptional
        No description.
        nullable
      • masked_email
        stringoptional
        No description.
        nullable
      • aadhaar_last4
        stringoptional
        No description.
        nullable
      • generated_at
        stringoptional
        No description.
        nullable
      • email_mobile_indicator
        stringoptional
        No description.
        nullable
      • reference_id
        stringoptional
        No description.
        nullable
    • photo
      objectoptional
      Server-decoded photo (PNG, base64).
      4 fields
      • format
        stringoptional
        No description.
        nullable
      • mime_type
        stringoptional
        No description.
        nullable
      • size_bytes
        integer (int32)optional
        No description.
      • base64
        stringoptional
        PNG bytes as base64 — usable directly as `data:image/png;base64,…`.
        nullable
    • signature
      objectoptional
      UIDAI RSA-2048 signature on the QR payload.
      3 fields
      • present
        booleanoptional
        No description.
      • verified
        booleanoptional
        Null when `verify_signature` was false. True/false when verification ran.
        nullable
      • base64
        stringoptional
        Raw 256-byte signature as base64. Empty when the QR carries no signature block.
        nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "qr_format": "string", "secure_qr_version": "string", "fields": { "name": "string", "dob": "string", "gender": "string", "care_of": "string", "house": "string", "street": "string", "landmark": "string", "location": "string", "vtc": "string", "sub_district": "string", "district": "string", "state": "string", "post_office": "string", "pincode": "string", "masked_mobile": "string", "masked_email": "string", "aadhaar_last4": "string", "generated_at": "string", "email_mobile_indicator": "string", "reference_id": "string" }, "photo": { "format": "string", "mime_type": "string", "size_bytes": 0, "base64": "string" }, "signature": { "present": false, "verified": false, "base64": "string" } }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
400Bad Request
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
500Internal Server Error
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X POST 'https://uat-ext.signcare.io/api/v1/aadhaar/secure-qr/read' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID' \ -H 'Content-Type: application/json' \ -d '{ "consent": "Y", "consent_text": "I hear by declare my consent agreement for fetching my information via SignCare API", "data": "", "payload": "", "data_content_type": "PNG", "include_photo": true, "verify_signature": false, "reference_id": "" }'