Convert PDFs to JSON in Zapier with Custom Extractors
Turn non-standard PDFs into JSON in Zapier: create a Cryvis custom extractor in Console, POST multipart file to /v1/custom-extractors/:slug, map data into Google Sheets or a webhook.
Standard invoices and receipts already have Cryvis built-ins. Everything else—utility bills, packing lists, inspection reports, vendor scorecards—needs a custom extractor: you define the JSON Schema once, then Zapier posts the PDF to POST https://api.cryvis.com/v1/custom-extractors/:slug.
There is still no generic PDF-to-JSON dump endpoint. Custom extractors give you a stable JSON contract for documents Cryvis does not ship as /v1/documents/*.
Hub: Zapier + Cryvis. Product: Custom extraction API. Schema reference: Custom extractor schemas. Console: Extractors.
When custom beats built-ins
Use a custom extractor when:
- The PDF is not an invoice, receipt, passport, PAN, Aadhaar, driver’s license, or Indian RC.
- You only need five fields from a dense form and want a closed schema (
additionalProperties: false). - Multiple vendors share an internal template your team already named.
Stay on built-ins when the document is that type — e.g. PAN or Indian RC. Mapping invoice field paths onto a custom response produces empty columns, not a Cryvis bug.
End-to-end flow
Console: create extractor + schema
|
v
slug assigned
|
v
Zapier: New File in Drive / Gmail attachment
|
v
Custom Request
POST /v1/custom-extractors/<slug>
multipart field: file
|
v
Filter: success = true
|
v
Google Sheets / webhookCredits: 1 per PDF page or 1 per image. MIME: PDF, JPEG, PNG, WebP. Auth: Bearer API key.
Step 1 — Schema in Console
- Name the extractor (e.g.
Utility bill). - Document description: short phrase injected into the model prompt (
residential utility bill). - Root schema:
type: object,properties, prefer nullable unions["string", "null"]/["number", "null"]. - Mark critical IDs
x-cryvis-validation: hard; noisy OCR fieldssoft. - Save and copy the slug from the extractor detail page (used in the URL path).
Minimal utility-bill schema:
{
"type": "object",
"properties": {
"account_number": {
"type": ["string", "null"],
"description": "Utility account or customer number",
"x-cryvis-validation": "hard"
},
"service_address": {
"type": ["string", "null"],
"description": "Service location as printed",
"x-cryvis-validation": "soft"
},
"billing_period_start": {
"type": ["string", "null"],
"format": "date",
"description": "Period start YYYY-MM-DD",
"x-cryvis-validation": "soft"
},
"billing_period_end": {
"type": ["string", "null"],
"format": "date",
"description": "Period end YYYY-MM-DD",
"x-cryvis-validation": "soft"
},
"amount_due": {
"type": ["number", "null"],
"description": "Total amount due",
"x-cryvis-validation": "soft"
},
"due_date": {
"type": ["string", "null"],
"format": "date",
"description": "Payment due date",
"x-cryvis-validation": "soft"
}
},
"required": ["account_number"],
"additionalProperties": false
}Upload a real sample in Console and inspect meta.validation.warnings before you trust Zapier mappings. Full schema docs: /docs/custom-extractors. API overview: /apis/custom.
Step 2 — Zap: PDF → JSON → Sheets
Steps
- Google Drive → New File in Folder (PDF / image filter if available)
- Ensure file binary is available (download / get file content)
- Webhooks by Zapier → Custom Request
- Filter by Zapier —
successExactly matchestrue - Google Sheets → Create Spreadsheet Row
or Webhooks by Zapier → POST to your own webhook with the JSON body
Custom Request
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://api.cryvis.com/v1/custom-extractors/YOUR_SLUG |
| Headers | Authorization: Bearer sk_live_... |
| Payload Type | Form / multipart |
Field file | Drive binary; filename from Drive |
After a test run, Zapier exposes success, data, meta as mappable fields.
Successful response shape
{
"success": true,
"data": {
"account_number": "48291033",
"service_address": "12 Oak St",
"billing_period_start": "2026-02-01",
"billing_period_end": "2026-02-28",
"amount_due": 94.2,
"due_date": "2026-03-15"
},
"meta": {
"document_type": "custom:YOUR_SLUG",
"request_id": "req_custom_01hxyz",
"validation": {
"is_valid": true,
"warnings": [],
"confidence": 1
}
}
}Hard validation failures return 422 with error.code VALIDATION_ERROR — not a partial data object. Soft failures stay 200 with warnings in meta.validation.
cURL:
curl -X POST https://api.cryvis.com/v1/custom-extractors/YOUR_SLUG \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@utility-bill.pdf"Step 3 — Map into Sheets
| Column | Mapping |
|---|---|
| Account | data.account_number |
| Address | data.service_address |
| Period start | data.billing_period_start |
| Amount due | data.amount_due |
| Due date | data.due_date |
| Valid? | meta.validation.is_valid |
| Request ID | meta.request_id |
Use Formatter / Paths to blank out literal null strings. Your data keys are only what you declared in the schema.
Webhook, Gmail, and validation
Instead of Sheets, POST JSON (data + meta.request_id) to your own endpoint with a second Custom Request. Gmail variant: Gmail → New Attachment (filter PDF) → same multipart file → Sheets.
| Validation | HTTP | Zap tip |
|---|---|---|
| Soft warning | 200 | Paths → Slack review if warnings length > 0 |
| Hard fail | 422 | Stop; fix schema/document; no Create Row |
| Bad key / no credits | 401 / 402 | Rotate token or pause Zap (Credits) |
Do not map invoice paths (seller, line_items) onto custom data — only schema keys exist. For ID documents use built-ins via Automate KYC.
Checklist: slug in URL from /console/extractors; Console sample test; multipart field exactly file; Sheets columns = schema keys; Filter on success.
Related reading
CTA
Define your schema in /console/extractors, then Custom Request POST file to https://api.cryvis.com/v1/custom-extractors/:slug with Bearer auth. Product: /apis/custom. Docs: /docs/custom-extractors. Map data.* into Sheets or forward JSON to your webhook.