← Back to blog

Extract Passport Data with Zapier and Cryvis

Two-file passport upload (first_page + last_page) to Cryvis, then map biodata and MRZ into Google Sheets or HubSpot.

Passport KYC fails in Zapier when the Custom Request sends the wrong multipart shape. Cryvis expects two parts — first_page (biodata) and last_page (final page) — not a single file. This Zap takes two uploads, extracts biodata + MRZ, and writes Sheets or HubSpot.

Hub: Zapier + Cryvis. Multipart cheat sheet: Send multipart form data. Sibling KYC: Extract Aadhaar.

Architecture

Source (pick one)
  - Google Drive: two files in a pair folder / naming convention
  - Forms: biodata upload + final-page upload
        |
        v
Filter: both binaries present
        |
        v
Webhooks by Zapier: Custom Request
  POST https://api.cryvis.com/v1/documents/passport
  multipart: first_page + last_page
  Authorization: Bearer <API_KEY>
        |
        v
Filter: success is true
        |
        v
Destination
  Google Sheets row  OR  HubSpot Create/Update Contact

Prerequisites

  • Cryvis API key with passport extraction — Passport API
  • Zapier account
  • Two clear page images/PDFs (biodata + final)
  • Destination: Google Sheet or HubSpot with custom properties

Credits: 1 per image/page — a dual-image call typically costs two credits. See Credits.

Wrong vs right multipart

# Wrong — will not match the API contract
part: file = passport.jpg

# Right
part: first_page = biodata.jpg
part: last_page  = final.jpg
PartRequiredContent
first_pageyesBiodata page
last_pageyesSecondary / MRZ page

There is no file field on passport. Do not reuse the invoice/receipt multipart pattern.

Step 1: Source files

Option A — Forms with two file questions (biodata + final page). Copy: “Upload biodata and final page. Both required.”

Option B — Drive: two files named predictably (e.g. passport-front.jpg, passport-back.jpg) or two consecutive uploads your Zap can pair. If you only have one Drive trigger, use a Form for KYC intake — pairing two independent Drive events is fragile.

Add a Filter before Cryvis: both binaries must exist. Passport calls with only one part return a client error.

Step 2: Call Cryvis Passport API

Webhooks by Zapier → Custom Request

PieceValue
MethodPOST
URLhttps://api.cryvis.com/v1/documents/passport
HeaderAuthorization: Bearer <API_KEY>
Bodymultipart/form-data
Part first_pagebiodata binary
Part last_pagefinal-page binary

MIME: application/pdf, image/jpeg, image/png, image/webp.

Docs: extractPassport.

Step 3: Response

{
  "success": true,
  "data": {
    "full_name": "JOHN DOE",
    "given_name": "JOHN",
    "surname": "DOE",
    "date_of_birth": "1995-04-12",
    "nationality": "IND",
    "passport_number": "P1234567",
    "issue_date": "2020-04-12",
    "expiry_date": "2030-04-12",
    "issuing_country": "IND",
    "sex": "M",
    "mrz": {
      "line1": "P<INDDOE<<JOHN<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
      "line2": "P1234567<3IND9504124M3004123<<<<<<<<<<<<<<<4",
      "check_digits_valid": true
    }
  },
  "meta": {}
}

Map:

FieldJSON path
Full namedata.full_name
Given / surnamedata.given_name / data.surname
DOBdata.date_of_birth
Nationalitydata.nationality
Passport #data.passport_number
Issue / expirydata.issue_date / data.expiry_date
Issuing countrydata.issuing_country
Sexdata.sex
MRZ validdata.mrz.check_digits_valid
MRZ linesdata.mrz.line1 / data.mrz.line2

Fields can be null on bad scans. Guard HubSpot updates so nulls do not wipe existing CRM data.

Step 4a: Google Sheets destination

Headers:

Full Name | Passport Number | DOB | Nationality | Issue | Expiry | Issuing Country | Sex | MRZ Valid | Source | Processed At

Create Spreadsheet Row from the paths above. Prefer storing mrz.check_digits_valid over raw MRZ lines in a shared Sheet.

Step 4b: HubSpot destination

Create custom properties once: passport_number, passport_nationality, passport_issue_date, passport_expiry_date, passport_issuing_country, passport_mrz_valid, kyc_status.

Create/Update Contact keyed by email from the Form:

HubSpotValue
First / Last namegiven_name / surname
passport_numberdata.passport_number
passport_mrz_validdata.mrz.check_digits_valid
kyc_statuspassport_extracted or passport_mrz_review

Paths after success:

Path A: mrz.check_digits_valid is true → kyc_status = passport_extracted
Path B: false → kyc_status = passport_mrz_review (+ optional Slack)

Optional expiry Filter: only mark extracted when expiry_date is far enough in the future for your policy.

Errors and testing

ErrorAction
401Fix Bearer token; stop
400Missing first_page/last_page or bad image — ask for resubmit
5xxReplay from Zap history after confirming Cryvis is up

Testing checklist

  1. Two clear JPEGs → success: true, non-null passport_number.
  2. Only biodata → expect failure; Filter should catch before HTTP when possible.
  3. Swapped pages → degraded fields / MRZ — biodata must be first_page.
  4. MRZ fail fixture → check_digits_valid: false → review Path.

Troubleshooting

SymptomLikely causeFix
Missing fileSent file instead of two partsUse first_page + last_page
Blank passport #Mapped outside dataUse data.passport_number
HubSpot wiped DOBNull overwriteSkip update when field is empty
High creditsTwo imagesExpected — budget dual-page KYC

Production notes

  • Do not put raw MRZ lines on every CRM contact layout; store the boolean and keep lines in a restricted Sheet or vault if fraud review needs them.
  • Swapping first_page and last_page is the most common silent quality bug.
  • Plan volume against Credits and Pricing.