Convert Receipts to JSON with Make.com
Wire Make.com HTTP modules to the Cryvis Receipt API: multipart uploads, single-file envelopes, and results[] batch responses mapped to downstream apps.
Most Make receipt tutorials jump straight to Sheets. This one stays on the HTTP boundary: how to call Cryvis correctly, how to read the response envelope, and how batch results[] differs from single-file data. Once the JSON path is solid, any destination module becomes boring wiring.
Index of Make posts: Make.com hub. Applied destinations: Drive → Sheets, Gmail → Airtable. Invoice HTTP twin: extract invoice data.
Workflow
[Trigger: Drive | Gmail | Webhook | Manual]
|
v
[Assemble file binary]
|
+-- single file --------------------+
| v
| [HTTP: Make a Request]
| POST .../receipt
| one multipart "file"
| |
| v
| data.* mapping
|
+-- multiple files -----------------+
v
[HTTP: Make a Request]
multiple "file" parts
|
v
results[] Iterator
|
v
item.data.* mappingModules
| Module | Purpose |
|---|---|
| Any file source | Produce binary + filename |
| HTTP — Make a Request | Cryvis call |
| Iterator | Required for results[] |
| JSON — Parse JSON | Only if you stored raw body as text |
| Router | Branch on success / HTTP status |
| Data Store | Optional cache of raw JSON by file hash |
| Error Handler | Auth vs validation vs upstream |
You do not need a Cryvis-specific Make app. Native HTTP is enough and keeps blueprints portable.
Cryvis HTTP configuration (canonical)
POST https://api.cryvis.com/v1/documents/receipt
Authorization: Bearer <API_KEY>
Content-Type: multipart/form-data; boundary=...In Make UI:
| Field | Value |
|---|---|
| URL | https://api.cryvis.com/v1/documents/receipt |
| Method | POST |
| Body type | Multipart/form-data |
| Fields | file → mapped binary (repeat file for batch) |
| Headers | Authorization: Bearer {{var.api_key}} |
| Parse response | Yes (JSON) |
| Timeout | Raise for multipage PDFs |
Auth is Bearer only for this endpoint. Do not send the key as a query param.
Allowed files: pdf, jpeg, png, webp. Credits: 1 per page/image. Batching does not discount — three images in one request still cost three credits.
Reference: extractReceipt docs. Product: Receipt API.
Curl parity check
Before debugging Make, prove the key works:
curl -X POST https://api.cryvis.com/v1/documents/receipt \
-H "Authorization: Bearer $CRYVIS_API_KEY" \
-F "file=@receipt-001.jpg" \
-F "file=@receipt-002.pdf"If curl succeeds and Make fails, the bug is almost always multipart field naming (file vs files) or missing binary.
Single-file response envelope
One file part → top-level data:
{
"success": true,
"data": {
"receipt_number": "RCT-2024-00891",
"transaction_date": "2024-06-15",
"transaction_time": "14:32:05",
"merchant": {
"name": "Fresh Mart Grocery",
"tax_id": "GB123456789"
},
"customer": null,
"location": {
"city": "London",
"country": "GB"
},
"payment": {
"method": "card",
"last4": "1881"
},
"currency": "GBP",
"line_items": [
{
"description": "Organic Bananas",
"quantity": 2,
"unit_price": 1.99,
"amount": 3.98
}
],
"subtotal": 5.28,
"tax_total": 0.42,
"total_amount": 5.55,
"amount_paid": 5.55,
"receipt_type": "grocery",
"notes": null
},
"meta": {}
}Map in Make as {{1.data.merchant.name}} style paths (bundle numbers vary). Always gate on success with a Filter before writing to a system of record.
Useful meta keys (when present) are for debugging — latency, page counts — not for finance columns. Prefer data.* for business fields.
Batch response: results[]
Multiple file parts → do not expect a single data object. Iterate results[]:
{
"success": true,
"results": [
{
"file_name": "receipt-001.jpg",
"data": { "merchant": { "name": "North Cafe" }, "total_amount": 28.4 },
"meta": {}
},
{
"file_name": "receipt-002.pdf",
"data": { "merchant": { "name": "Fresh Mart" }, "total_amount": 5.55 },
"meta": {}
}
]
}Make pattern:
- HTTP module returns the batch body.
- Iterator →
results. - Inside the loop, map
file_name,data.merchant.name,data.total_amount, etc. - Per-item Error Handler if one file’s
datais null but others succeed.
Partial failure modes matter: some batches mark overall success while an individual result needs review. Inspect each data object; do not assume uniformity.
When to batch vs loop scenarios
| Approach | Use when |
|---|---|
| One HTTP call per file | Simpler mapping; better incomplete-execution retries per file |
One HTTP call, many file parts | Fewer round trips; must Iterator results[] |
For Gmail with three attachments, per-file HTTP calls are usually easier to reason about in Make. For a Drive folder zip-extract that already has an array of binaries, batch can be fine.
Response mapping patterns
Flatten for Sheets / Airtable
merchant_name <- data.merchant.name
merchant_tax_id <- data.merchant.tax_id
txn_date <- data.transaction_date
txn_time <- data.transaction_time
currency <- data.currency
subtotal <- data.subtotal
tax_total <- data.tax_total
total_amount <- data.total_amount
amount_paid <- data.amount_paid
receipt_number <- data.receipt_number
receipt_type <- data.receipt_type
payment_method <- data.payment.method
city <- data.location.city
notes <- data.notesKeep nested JSON
Some teams store the entire data object in a JSON column (Postgres, BigQuery load via webhook). In Make, pass data as stringified JSON into the destination. That preserves line_items without an Iterator.
Line items Iterator
[HTTP]
-> [Iterator: data.line_items]
-> [Destination: create child row]Map description, quantity, unit_price, amount. Parent key = receipt_number + transaction_date.
Errors at the HTTP layer
| Signal | Meaning | Make action |
|---|---|---|
| HTTP 401/403 | Bad/missing Bearer token | Break; fix connection |
| HTTP 400 | Multipart/MIME problem | Log raw request config; no retry |
| HTTP 413 | Payload too large | Compress or split PDF |
| HTTP 429 | Rate / quota | Incomplete execution + delay |
| HTTP 5xx | Upstream | Retry with backoff |
success: false | Soft failure with body | Router to dead-letter store |
Enable “Show advanced settings” → store headers and body on error. You want the Cryvis error message, not only Make’s generic failure text.
Test plan
- Single JPEG via Manual trigger + HTTP — assert
data.total_amount. - Single PDF — assert credits/pages mental model matches pages in the file.
- Two files in one multipart request — assert
resultslength 2 and Iterator emits two bundles. - Rename multipart field to
fileson purpose — confirm failure; rename back tofile. - Wrong API key — confirm 401 path.
- Compare Make JSON to curl JSON for the same file; they should match field-for-field.
Mapping gotchas
Nested objects (merchant, payment, location) arrive as objects in Make’s mapper. If a destination needs flat strings, map leaf paths explicitly — do not cast the whole merchant object to text or you will store [Object object]-style junk.
Empty vs missing: Treat null receipt_number as blank, not the string "null". Use an if-empty formula in Set Variable before writing.
line_items may be []. An Iterator on an empty array simply emits zero bundles — your parent record should still write. Guard child-table logic with a Filter length(line_items) > 0 only when you want to skip empty iterations noiselessly.
Numeric fields (subtotal, tax_total, total_amount, amount_paid) should stay numbers through Make. Coercing to text early breaks Sheets SUM and Airtable currency rollups.
Production checklist
- API keys in Make connections; separate keys for staging vs production.
- Logging: Data Store or Sheets
Http Logwithfile_name, status,receipt_number, credit estimate. - Idempotency key strategy when replaying incomplete executions (receipt to Google Sheets).
- Do not log full Bearer tokens in error emails.
- Version your scenario: cloning to “Receipt HTTP v2” before changing batch behavior.
- Document whether each consumer expects single
dataor batchresults[]so future edits do not break mapping.
Related guides
CTA
Treat receipt OCR as infrastructure: multipart in, JSON out. Hook Make’s HTTP module to the Cryvis Receipt API and keep mapping logic in your scenario — details in extractReceipt.