← Back to blog

Extract Passport Data with Make.com and Cryvis

Typeform passport uploads to Cryvis POST /v1/documents/passport with first_page and last_page, then map biodata and MRZ fields into HubSpot.

Passport KYC fails in automation when the HTTP module sends the wrong multipart shape. Cryvis expects two parts — first_page (biodata) and last_page (final page) — not a single file. This tutorial builds a Make.com scenario from Typeform through Cryvis passport extraction into HubSpot contact properties, including MRZ check-digit handling.

Broader multi-doc KYC: Automate KYC in Make.com. Webhook sequential checks: No-code KYC workflow.

Architecture

Typeform
  - email
  - passport biodata upload
  - passport final-page upload
        |
        v
Make.com
  HTTP Get files
        |
        v
POST https://api.cryvis.com/v1/documents/passport
  multipart: first_page + last_page
  header: Authorization: Bearer ...
        |
        v
HubSpot Contact
  name, DOB, passport #, expiry, MRZ flag

Prerequisites

  • Cryvis API key with access to passport extraction
  • Make.com account
  • Typeform with two file uploads
  • HubSpot private app / connection with permission to create contacts and custom properties

Step 1: Typeform fields

QuestionTypeNotes
Work emailEmailHubSpot match key
Passport biodata pageFilePhoto page with MRZ usually on the same or facing page depending on country booklet layout — still send as first_page
Passport final / back pageFileMaps to last_page
ConsentYes/NoStore for compliance; not sent to Cryvis

Copy text in Typeform: “Upload a clear photo or PDF of the biodata page and the final page. Both are required.”

Step 2: Watch Typeform in Make

Typeform → Watch Responses. Map email and both file answers. If Typeform exposes URLs, chain HTTP → Get a file twice:

first_page_bin = download(biodata_url)
last_page_bin  = download(final_url)

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

Step 3: Call Cryvis Passport API

HTTP → Make a request

ParameterValue
URLhttps://api.cryvis.com/v1/documents/passport
MethodPOST
HeadersAuthorization: Bearer {{cryvis_api_key}}
Body typeMultipart/form-data
Multipart fieldMaps to
first_pagefirst_page_bin (required)
last_pagelast_page_bin (required)

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

Wrong vs right

# Wrong — will not match the OpenAPI contract
-F "file=@passport.jpg"

# Right
-F "first_page=@biodata.jpg" \
-F "last_page=@final.jpg"

Step 4: Read the response

On success:

{
  "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
    }
  }
}

In Make, parse JSON (HTTP module can parse automatically). Bind:

Make variableJSON path
full_namedata.full_name
given_namedata.given_name
surnamedata.surname
date_of_birthdata.date_of_birth
nationalitydata.nationality
passport_numberdata.passport_number
issue_datedata.issue_date
expiry_datedata.expiry_date
issuing_countrydata.issuing_country
sexdata.sex
mrz_line1data.mrz.line1
mrz_line2data.mrz.line2
mrz_okdata.mrz.check_digits_valid

Nullability: fields can be null if OCR cannot read a value. Guard HubSpot updates with IFEMPTY so you do not overwrite good CRM data with blanks on a bad scan.

Official docs: /docs/api/extractPassport. API overview: /apis/passport.

Step 5: HubSpot contact mapping

Create custom properties (once):

  • passport_number (single-line)
  • passport_nationality (single-line)
  • passport_issue_date (date)
  • passport_expiry_date (date)
  • passport_issuing_country (single-line)
  • passport_mrz_valid (boolean)
  • kyc_status (dropdown)

HubSpot → Create/Update a Contact keyed by email:

HubSpotValue
EmailTypeform email
First namegiven_name
Last namesurname
Date of birthdate_of_birth (if you use HubSpot DOB)
passport_numberpassport_number
passport_nationalitynationality
passport_issue_dateissue_date
passport_expiry_dateexpiry_date
passport_issuing_countryissuing_country
passport_mrz_validmrz_ok
kyc_statuspassport_extracted or passport_mrz_review

Status rule:

IF mrz_ok = true
  THEN kyc_status = passport_extracted
  ELSE kyc_status = passport_mrz_review

Optional: create a HubSpot task for ops when mrz_ok is false. Do not auto-approve expired passports — compare expiry_date to now in a Filter if your policy requires a future expiry.

Step 6: Expiry gate (optional)

After Cryvis:

Filter: parseDate(expiry_date) > addDays(now; 30)
  YES -> kyc_status = passport_extracted
  NO  -> kyc_status = passport_expiring_or_expired

This is Make-side logic; Cryvis returns the date string, it does not enforce your business rule.

Step 7: Errors and retries

Attach an Error handler to the HTTP module:

ErrorAction
401Alert “Cryvis auth failed”; stop
400Alert “multipart missing first_page/last_page or bad image”; ask applicant to resubmit via Typeform
5xxEnable Make retry (2–3 attempts) then ops Slack

Never retry endlessly on 400 — fix the payload.

Testing checklist

  1. Submit two clear JPEG pages → expect success: true and non-null passport_number.
  2. Submit only biodata → expect failure; confirm Filter catches it before HTTP.
  3. Blurry scan → possible nulls; confirm HubSpot does not clear existing properties.
  4. MRZ-tampered fixture (if you have one) → check_digits_valid: false → HubSpot review status.

Credit note

Passport extraction is metered per image (see pricing on /apis/passport). Two pages means two image credits for a typical dual-image call — plan Typeform volume accordingly.

Make.com mapping pitfalls

  1. Mapping Typeform file URL into multipart as Text — Cryvis needs the downloaded binary. Always Get a file first.
  2. Swapping first_page and last_page — biodata must be first_page. Swapped pages degrade fields and MRZ.
  3. Reading passport_number instead of data.passport_number — response wrapper includes success and data.
  4. Overwriting HubSpot DOB with null — use IFEMPTY guards on every mapped property.

Storing MRZ lines

Most CRMs do not need mrz.line1 / line2 on the contact. Prefer:

  • Contact: passport_mrz_valid boolean
  • Private Data store or secure vault: raw MRZ lines if fraud review needs them

That keeps HubSpot layouts clean and reduces PII sprawl.

Resubmission loop

When kyc_status = passport_mrz_review or expiry fails:

  1. HubSpot workflow sends Typeform partial-submit link
  2. New Watch Responses run creates a fresh Cryvis call
  3. Update contact; set kyc_status = passport_extracted only when mrz_ok and expiry policy pass

Keep the previous passport number in a passport_number_previous property for audit when the number changes (reissue).

CTA

Extract passport biodata and MRZ with Cryvis: /apis/passport · Docs: /docs/api/extractPassport. In Make, POST multipart first_page + last_page to https://api.cryvis.com/v1/documents/passport with Authorization: Bearer.