No-Code KYC Workflow in Make.com: Webhook to Sequential ID Checks and Airtable
Build a Make.com KYC pipeline from a custom webhook through sequential Cryvis Passport, PAN, and Aadhaar checks into an Airtable applicant record.
Not every KYC intake lives in Typeform. Product backends, mobile apps, and partner portals often POST applicant packets to an automation layer. This guide builds a Make.com scenario that starts on a Custom webhook, runs sequential Cryvis identity checks, and writes a normalized applicant row to Airtable.
Sequential (not parallel) checks are useful when later steps depend on earlier results — for example, skip Aadhaar if passport already satisfies policy, or require PAN only for India-resident applicants.
For a Typeform + Router pattern into HubSpot, see Automate KYC in Make.com. Single-document deep dives: passport, PAN, Aadhaar.
Architecture
Your app / portal
|
| POST multipart or JSON+URLs
v
Make.com Custom Webhook
|
v
[1] Passport? --> Cryvis /v1/documents/passport
|
v
[2] PAN? --> Cryvis /v1/documents/pan
|
v
[3] Aadhaar? --> Cryvis /v1/documents/aadhaar
|
v
Airtable: ApplicantsAuth on every Cryvis call:
Authorization: Bearer YOUR_API_KEY
Base URL: https://api.cryvis.comStep 1: Design the webhook contract
Add Webhooks → Custom webhook and copy the URL into your app. Prefer a JSON body that references file URLs your scenario can download, or have the client POST multipart directly to Make.
Example JSON contract:
{
"applicant_id": "app_ ev_01",
"email": "rahul@example.com",
"country": "IND",
"checks": ["passport", "pan", "aadhaar"],
"files": {
"passport_first_page": "https://storage.example/p1.jpg",
"passport_last_page": "https://storage.example/p2.jpg",
"pan": "https://storage.example/pan.jpg",
"aadhaar_front": "https://storage.example/aadhaar-f.jpg",
"aadhaar_back": "https://storage.example/aadhaar-b.jpg"
}
}Keep checks explicit. Guessing document type from filenames is brittle; see identity document processing if you must classify by MIME/filename.
Step 2: Download binaries
For each URL present, add HTTP → Get a file:
| Variable | Source URL |
|---|---|
passport_first | files.passport_first_page |
passport_last | files.passport_last_page |
pan_file | files.pan |
aadhaar_1 | files.aadhaar_front |
aadhaar_2 | files.aadhaar_back |
Store Make file handles; do not pass remote URLs to Cryvis multipart fields — Cryvis expects uploaded binary parts.
Step 3: Airtable base
Create an Applicants table:
| Field | Type |
|---|---|
| Applicant ID | Single line (primary) |
| Country | Single line |
| Passport number | Single line |
| Passport name | Single line |
| Passport expiry | Date |
| MRZ valid | Checkbox |
| PAN number | Single line |
| PAN holder type | Single line |
| Aadhaar number | Single line |
| Aadhaar masked | Checkbox |
| Aadhaar address | Long text |
| KYC stage | Single select |
| Last error | Long text |
KYC stage options: received, passport_done, pan_done, aadhaar_done, complete, blocked.
Step 4: Create the Airtable row early
Right after the webhook, Airtable → Create a record with:
- Applicant ID, Email, Country
- KYC stage =
received
Updating one record through the chain keeps ops dashboards live as each check finishes.
Step 5: Sequential passport check
Router / Filter: continue only if checks contains passport and both passport URLs were provided.
HTTP → Make a request
| Setting | Value |
|---|---|
| URL | https://api.cryvis.com/v1/documents/passport |
| Method | POST |
| Body | Multipart/form-data |
| Header | Authorization: Bearer {{cryvis_api_key}} |
Multipart (names are required exactly as shown):
| Part | Binary |
|---|---|
first_page | passport_first |
last_page | passport_last |
Do not send a single file part — the passport endpoint rejects that shape.
Parse data:
passport_number = data.passport_number
passport_full_name = data.full_name
given_name = data.given_name
surname = data.surname
date_of_birth = data.date_of_birth
nationality = data.nationality
issue_date = data.issue_date
expiry_date = data.expiry_date
issuing_country = data.issuing_country
sex = data.sex
mrz_line1 = data.mrz.line1
mrz_line2 = data.mrz.line2
mrz_valid = data.mrz.check_digits_validAirtable → Update a record (same Applicant ID):
- Passport fields above
- KYC stage →
passport_done - If
mrz_validis false → KYC stageblocked, Last errorpassport_mrz_invalid
Policy fork: some teams continue to PAN/Aadhaar even when MRZ fails; others stop. Encode the choice as a Filter after this step.
API reference: /docs/api/extractPassport · /apis/passport.
Step 6: Sequential PAN check
Filter: checks contains pan AND country is IND (or your rule) AND KYC stage is not blocked.
HTTP
| Setting | Value |
|---|---|
| URL | https://api.cryvis.com/v1/documents/pan |
| Multipart | file = pan_file |
Map from data:
pan_number = data.pan_number
pan_full_name = data.full_name
father_name = data.father_name
pan_dob = data.date_of_birth
holder_type_code = data.holder_type_code
holder_type = data.holder_type
# optional audit
pan_series = data.pan_structure.series
pan_check_digit = data.pan_structure.check_digitUpdate Airtable → stage pan_done. Optionally compare pan_full_name to passport_full_name with a simple uppercase equality check; on mismatch set Last error name_mismatch_pan_passport without inventing fuzzy-match APIs Cryvis does not provide.
Docs: /docs/api/extractPan · /apis/pan.
Step 7: Sequential Aadhaar check (PII)
Filter: checks contains aadhaar AND stage not blocked.
HTTP
| Setting | Value |
|---|---|
| URL | https://api.cryvis.com/v1/documents/aadhaar |
| Multipart | file = aadhaar_1; optional second file = aadhaar_2 |
Map:
aadhaar_number = data.aadhaar_number
aadhaar_name = data.full_name
dob = data.date_of_birth
yob = data.year_of_birth
gender = data.gender
care_of = data.care_of
address = data.address
pincode = data.pincode
is_masked = data.is_maskedPII practices for Airtable:
- Enable field-level permissions; hide
Aadhaar numberfrom general editors. - Prefer writing the value only when needed; if
is_maskedis true, store as-is and check Aadhaar masked. - Do not mirror full Aadhaar into Make.com Data stores or public Slack messages.
- If your compliance pack forbids plain-text UID storage, write only last-4 derived in a Formula field and leave the raw field empty after a short TTL process outside Make.
Update Airtable → stage aadhaar_done, then a final module sets stage complete when all requested checks succeeded.
Docs: /docs/api/extractAadhaar · /apis/aadhaar.
Step 8: Webhook response
Enable Webhooks → Webhook response so your app gets a synchronous summary:
{
"applicant_id": "app_ev_01",
"kyc_stage": "complete",
"passport_number": "P1234567",
"pan_number": "ABCPS1234F",
"aadhaar_masked": true,
"mrz_valid": true
}Omit raw Aadhaar from the HTTP response if the caller does not need it — Airtable remains the system of record.
Error handler pattern
Each Cryvis HTTP
|
+-- Success path --> Airtable update
|
+-- Error handler
|
+-- Airtable: Last error = status + message
+-- KYC stage = blocked
+-- Webhook response 502/424 with applicant_idTypical root causes: missing last_page on passport, empty file on PAN, more than two Aadhaar files, expired Bearer token.
When to use sequential vs Router
| Pattern | Use when |
|---|---|
| Sequential (this post) | Later checks depend on earlier outcomes; single applicant packet |
| Router by type (automate KYC) | One submission is exactly one document type |
| MIME/filename Router (identity processing) | Unlabeled folder drops |
Related reading
- Automate KYC: Typeform → HubSpot
- Extract driver's license data
- All Make.com posts: /blog/make-com
CTA
Wire sequential Passport, PAN, and Aadhaar extraction with Cryvis: start at /apis/passport, /apis/pan, and /apis/aadhaar. Point Make's Custom webhook at your app, use Bearer auth against https://api.cryvis.com, and keep Airtable as the KYC ledger.