← Back to blog

Extract PAN Card Data with n8n: Google Drive to Google Sheets

Watch an India-KYC Drive folder, call Cryvis POST /v1/documents/pan with multipart $binary file, and append PAN number, name, and holder type to Google Sheets.

Indian KYC packs often land as PAN card scans in a shared Drive folder before anyone opens a CRM. This n8n workflow watches an India-KYC folder, sends each new file to Cryvis POST /v1/documents/pan, and appends structured rows to Google Sheets — including holder_type and pan_structure fields you can use for Individual vs Company routing.

Multi-document HubSpot flow: Automate KYC. Driver license companion: Extract driver's license data. Hub: /blog/n8n.

Architecture

Google Drive
  /India-KYC/pan-inbox/
        |
        v  (Trigger)
n8n workflow
        |
        v
POST https://api.cryvis.com/v1/documents/pan
  multipart: file = $binary.data
  Authorization: Bearer ...
        |
        v
Google Sheets
  pan_number | full_name | father_name | dob | holder_type | ...

Prerequisites

  • Cryvis API key (Header Auth credential in n8n)
  • Google Drive folder dedicated to PAN uploads (isolate from Aadhaar/passport)
  • Google Sheet with a header row
  • n8n Google Drive + Sheets + HTTP Request nodes

Step 1: Folder and Sheet schema

Drive path example: India-KYC / pan-inbox. Optionally move processed files to pan-done to avoid re-processing.

Sheet columns:

ColumnCryvis data field
A processed_at{{ $now.toISO() }}
B drive_file_idDrive trigger
C file_nameDrive trigger
D pan_number{{ $json.data.pan_number }}
E full_name{{ $json.data.full_name }}
F father_name{{ $json.data.father_name }}
G date_of_birth{{ $json.data.date_of_birth }}
H holder_type_code{{ $json.data.holder_type_code }}
I holder_type{{ $json.data.holder_type }}
J series{{ $json.data.pan_structure.series }}
K name_initial{{ $json.data.pan_structure.name_initial }}
L sequence{{ $json.data.pan_structure.sequence }}
M check_digit{{ $json.data.pan_structure.check_digit }}
N statusok / error

Step 2: Watch Drive and download binary

Google Drive Trigger → File Created in Folder

  • Folder: pan-inbox
  • Restrict to images/PDF if the trigger allows MIME filters

Downstream: Google Drive → Download File so the item has $binary.data. Watching metadata alone is not enough for multipart upload.

Confirm binary is present before HTTP:

{{ $binary.data }}   // must exist
{{ $binary.data.mimeType }}  // image/jpeg, application/pdf, ...

Step 3: Call Cryvis PAN

HTTP Request

SettingValue
MethodPOST
URLhttps://api.cryvis.com/v1/documents/pan
AuthenticationHeader Auth → Authorization: Bearer <API_KEY>
Body Content TypeForm-Data / Multipart
Response FormatJSON
Body ParameterTypeValue
filen8n Binary FileInput Binary Field = data

Supported types: PDF, JPEG, PNG, WebP. Field name must be exactly file (lowercase).

cURL equivalent:

curl -X POST https://api.cryvis.com/v1/documents/pan \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@pan-card.jpg"

Step 4: Response fields

{
  "success": true,
  "data": {
    "pan_number": "ABCPS1234F",
    "full_name": "RAHUL SHARMA",
    "father_name": "SURESH SHARMA",
    "date_of_birth": "1992-08-14",
    "holder_type_code": "P",
    "holder_type": "Individual",
    "pan_structure": {
      "series": "ABC",
      "holder_type_code": "P",
      "holder_type": "Individual",
      "name_initial": "S",
      "sequence": "1234",
      "check_digit": "F"
    }
  }
}

After HTTP Request, expressions read under $json.data.*:

{{ $json.data.pan_number }}
{{ $json.data.full_name }}
{{ $json.data.holder_type_code }}
{{ $json.data.pan_structure.series }}

holder_type_code is the 4th character semantics (e.g. P = Individual, C = Company). Use holder_type for human-readable Sheets filters.

Docs: /docs/api/extractPan · Product: /apis/pan.

Step 5: Append to Sheets

Google Sheets → Append Row

Map each column from Step 1. Nested structure:

series       = {{ $json.data.pan_structure.series }}
name_initial = {{ $json.data.pan_structure.name_initial }}
sequence     = {{ $json.data.pan_structure.sequence }}
check_digit  = {{ $json.data.pan_structure.check_digit }}

Set status = ok on the success path. Gate with IF:

{{ $json.success }} equals true

Step 6: Route by holder type

Add a Switch (or IF) after a successful extract:

OutputConditionAction
Individual{{ $json.data.holder_type_code }} = POptional Slack “retail KYC”
Company{{ $json.data.holder_type_code }} = CFlag for entity KYC pack
OtherfallbackOps review

You are not calling another Cryvis endpoint here — only branching on returned enums.

Step 7: Move processed files

Google Drive → Move File from pan-inboxpan-done. Prevents the trigger from re-ingesting the same scan.

Keep Drive file id from the trigger item. If a Set/Code node stripped binary but you still need the id, use a Merge (Combine by position) of the Drive metadata item with the HTTP response, or store drive_file_id in a Set node before HTTP.

Step 8: Errors

Attach an Error Trigger workflow (or Error Workflow on the HTTP node):

  1. Write a Sheets row with status=error, pan_number blank, file_name set.
  2. Move the file to pan-failed instead of pan-done.
  3. Alert Slack with Drive link only — not the image bytes.

Common issues:

  • Wrong field name (document instead of file)
  • Sending a URL string instead of $binary.data
  • HEIC uploads (convert before Cryvis, or reject with IF on mimeType)
  • Multi-page PDF of an entire KYC pack — prefer a cropped PAN-only file

Deduping and credits

Optional Sheets lookup on {{ $json.data.pan_number }}: Update if found, Append if not. Guard with IF {{ $json.data.pan_number.length }} Equal to 10. This workflow is extraction, not NSDL verification.

PAN is 1 credit per PDF page or image (Credits). Crop multi-page KYC packs to the PAN page. Move to pan-done so the trigger does not re-queue.

CTA

Extract Indian PAN cards to JSON with Cryvis: /apis/pan. Docs: /docs/api/extractPan. From n8n, POST $binary.data as multipart file to https://api.cryvis.com/v1/documents/pan with Bearer auth and append {{ $json.data.pan_number }}, {{ $json.data.full_name }}, and {{ $json.data.holder_type }} to Sheets.