← Back to blog

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.* mapping

Modules

ModulePurpose
Any file sourceProduce binary + filename
HTTP — Make a RequestCryvis call
IteratorRequired for results[]
JSON — Parse JSONOnly if you stored raw body as text
RouterBranch on success / HTTP status
Data StoreOptional cache of raw JSON by file hash
Error HandlerAuth 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:

FieldValue
URLhttps://api.cryvis.com/v1/documents/receipt
MethodPOST
Body typeMultipart/form-data
Fieldsfile → mapped binary (repeat file for batch)
HeadersAuthorization: Bearer {{var.api_key}}
Parse responseYes (JSON)
TimeoutRaise 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:

  1. HTTP module returns the batch body.
  2. Iteratorresults.
  3. Inside the loop, map file_name, data.merchant.name, data.total_amount, etc.
  4. Per-item Error Handler if one file’s data is 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

ApproachUse when
One HTTP call per fileSimpler mapping; better incomplete-execution retries per file
One HTTP call, many file partsFewer 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.notes

Keep 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

SignalMeaningMake action
HTTP 401/403Bad/missing Bearer tokenBreak; fix connection
HTTP 400Multipart/MIME problemLog raw request config; no retry
HTTP 413Payload too largeCompress or split PDF
HTTP 429Rate / quotaIncomplete execution + delay
HTTP 5xxUpstreamRetry with backoff
success: falseSoft failure with bodyRouter 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

  1. Single JPEG via Manual trigger + HTTP — assert data.total_amount.
  2. Single PDF — assert credits/pages mental model matches pages in the file.
  3. Two files in one multipart request — assert results length 2 and Iterator emits two bundles.
  4. Rename multipart field to files on purpose — confirm failure; rename back to file.
  5. Wrong API key — confirm 401 path.
  6. 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 Log with file_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 data or batch results[] so future edits do not break mapping.

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.