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 ContactPrerequisites
- 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| Part | Required | Content |
|---|---|---|
first_page | yes | Biodata page |
last_page | yes | Secondary / 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
| Piece | Value |
|---|---|
| Method | POST |
| URL | https://api.cryvis.com/v1/documents/passport |
| Header | Authorization: Bearer <API_KEY> |
| Body | multipart/form-data |
Part first_page | biodata binary |
Part last_page | final-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:
| Field | JSON path |
|---|---|
| Full name | data.full_name |
| Given / surname | data.given_name / data.surname |
| DOB | data.date_of_birth |
| Nationality | data.nationality |
| Passport # | data.passport_number |
| Issue / expiry | data.issue_date / data.expiry_date |
| Issuing country | data.issuing_country |
| Sex | data.sex |
| MRZ valid | data.mrz.check_digits_valid |
| MRZ lines | data.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:
| HubSpot | Value |
|---|---|
| First / Last name | given_name / surname |
passport_number | data.passport_number |
passport_mrz_valid | data.mrz.check_digits_valid |
kyc_status | passport_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
| Error | Action |
|---|---|
| 401 | Fix Bearer token; stop |
| 400 | Missing first_page/last_page or bad image — ask for resubmit |
| 5xx | Replay from Zap history after confirming Cryvis is up |
Testing checklist
- Two clear JPEGs →
success: true, non-nullpassport_number. - Only biodata → expect failure; Filter should catch before HTTP when possible.
- Swapped pages → degraded fields / MRZ — biodata must be
first_page. - MRZ fail fixture →
check_digits_valid: false→ review Path.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Missing file | Sent file instead of two parts | Use first_page + last_page |
| Blank passport # | Mapped outside data | Use data.passport_number |
| HubSpot wiped DOB | Null overwrite | Skip update when field is empty |
| High credits | Two images | Expected — 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_pageandlast_pageis the most common silent quality bug. - Plan volume against Credits and Pricing.