← Back to blog

Extract Invoice Data into Google Sheets with Zapier

Build a Zap: Google Drive New File → Cryvis Invoice OCR → Google Sheets. Map invoice_number, dates, seller, and totals.

What we're building

A Drive folder drops invoice PDFs or images. Zapier posts each file to Cryvis, then appends one Sheets row with header fields — number, dates, vendor, totals. One document → one row (no line-item expansion).

Google Drive: New File in Folder
        |
        v
(Download / file binary available to next step)
        |
        v
Webhooks by Zapier: Custom Request
  POST https://api.cryvis.com/v1/documents/invoice
  Authorization: Bearer <API_KEY>
  multipart part: file = binary
        |
        v
Filter: success is true
        |
        v
Google Sheets: Create Spreadsheet Row
  invoice_number | invoice_date | due_date | currency
  seller.name | buyer.name | subtotal | tax_total
  total_amount | amount_due | file name

Hub: Zapier + Cryvis. Multipart field names: Send multipart form data.

What you need

  • Zapier account
  • Google Drive folder for inbound invoices
  • Google Sheet with a header row (columns in the destination section)
  • Cryvis API key — Invoice API, docs at /docs/api/extractInvoice
  • Sample invoice PDF or JPEG/PNG/WebP

Credits: 1 per PDF page or image. See Credits.

Create the Zap

Add steps in this order:

  1. Google Drive — New File in Folder (Trigger)
  2. Webhooks by Zapier — Custom Request (Action)
  3. Filter — continue only when extraction succeeded
  4. Google Sheets — Create Spreadsheet Row (Action)

Optional later: MIME Filter after Drive, Paths for amount thresholds, archive/move after success.

Configure source

New File in Folder

  • Connect the Google account that owns (or can read) the inbox folder
  • Pick the inbound folder only — do not watch the whole Drive
  • Prefer uploaded PDFs/images; skip Google Docs natives unless you convert to PDF first

While testing, keep one sample file in the folder and use Zapier’s test / Zap history so you are not burning credits on every Drive sync.

Configure Cryvis HTTP

Webhooks by Zapier → Custom Request — required HTTP shape:

PieceValue
MethodPOST
URLhttps://api.cryvis.com/v1/documents/invoice
HeaderAuthorization: Bearer <API_KEY>
Bodymultipart/form-data
Part namefile
Part valuebinary from the Drive trigger / download step

Do not force Content-Type: application/json. The multipart boundary must come from the HTTP client.

Accepted MIME: pdf, jpeg, png, webp. This Zap sends one file per run.

If the Custom Request returns a string body, confirm Zapier parsed JSON (or use a Formatter step) before mapping. Check success is boolean true, not the string "true".

Process response

Cryvis returns roughly:

{
  "success": true,
  "data": {
    "invoice_number": "INV-2024-00142",
    "invoice_date": "2024-03-15",
    "due_date": "2024-04-14",
    "currency": "USD",
    "seller": { "name": "Acme Cloud Services", "tax_id": "US12-3456789" },
    "buyer": { "name": "Globex Corporation" },
    "line_items": [],
    "subtotal": 1148.98,
    "tax_total": 94.79,
    "total_amount": 1193.77,
    "amount_due": 1193.77
  },
  "meta": {}
}

Map from data.*:

  • data.invoice_number
  • data.invoice_date
  • data.due_date
  • data.currency
  • data.seller.name
  • data.buyer.name
  • data.subtotal
  • data.tax_total
  • data.total_amount
  • data.amount_due

Nested objects need dotted paths. Do not dump the whole seller object into one cell unless you want raw JSON.

Configure destination

Sheet headers (row 1):

Invoice Number | Invoice Date | Due Date | Currency | Vendor | Buyer | Subtotal | Tax | Total | Amount Due | Source File | Processed At

Create Spreadsheet Row mapping:

ColumnSource
Invoice Numberdata.invoice_number
Invoice Datedata.invoice_date
Due Datedata.due_date
Currencydata.currency
Vendordata.seller.name
Buyerdata.buyer.name
Subtotaldata.subtotal
Taxdata.tax_total
Totaldata.total_amount
Amount Duedata.amount_due
Source FileDrive file name
Processed AtZapier timestamp / Formatter “now”

Prefer Airtable instead? Use Invoice to Airtable.

Filters and error handling

Filter after Drive (recommended): continue only if filename ends with .pdf, .jpg, .jpeg, .png, or .webp.

Filter after Custom Request:

Only continue if: Custom Request → success (boolean) is true

On hard failures, open Zap history for the Custom Request step — status code and body snippet tell you whether auth, MIME, or mapping broke. For Slack alerts on high amounts, see Automate invoice processing.

Troubleshooting

SymptomLikely causeFix
401 / 403Bad or missing Bearer tokenHeader must be Authorization: Bearer … with a single space
Validation / missing fileWrong part name or JSON bodyMultipart part must be file, not document or attachment
Empty Sheets rowMapping from top-level instead of dataUse data.invoice_number, not bare invoice_number
Duplicate rowsDrive re-fires on same fileMove to Processed after success; Filter on invoice_number if needed
Credits higher than expectedMulti-page PDFEach page = 1 credit — Credits

Test the Custom Request alone in Zap history before wiring Sheets. That separates API issues from mapping bugs.

Production notes

  • Archive after success: move the Drive file to /Invoices/Processed so New File does not re-pick the same PDF.
  • Keep the trigger folder quiet — no drafts, exports, or spreadsheets.
  • Multi-page PDFs: each page costs 1 credit; huge statements add up.
  • Idempotency: Filter on known invoice_number values if vendors resend the same PDF.

Next steps

Gmail intake with Slack: Automate invoice processing. Airtable AP base: Invoice to Airtable. Multipart reference: Send multipart form data.

API: extractInvoice · Product: Invoice · Hub: Zapier + Cryvis · Sign up · Pricing.