Skip to main content

Implementation Guide: Veryfi Email Collector + Custom Headers

Overview

Your end users email receipts to one address you own (for example receipts@yourorg.com). Your forwarding inbox tags each email with an ID for the user and the submission in a custom X-External-ID header and forwards it to your Veryfi Email Collector address (yourname@veryfi.cc). Veryfi filters, extracts and validates the document, then notifies you by webhook. Your system fetches the JSON, which carries the same ID in external_id, and saves the receipt to the right user and submission.

What you build: the shared inbox, a small forwarding service that matches the sender and adds headers, a webhook endpoint, and storage.

What Veryfi runs: email intake, spam and virus filtering, picking the receipt from the body or attachments, extraction, validation and enrichment, automatic retries, and notifications.

Choose this option when you want the least code to build and are happy to receive results asynchronously. If you need results in real time, full control over which parts of an email are processed, or your own retry logic, use the direct API option instead (see the Shared Inbox + Veryfi API guide).

This guide: Veryfi Email Collector

Alternative: your own collector + Veryfi API

How email reaches Veryfi

You forward each email to your veryfi.cc address

Your service sends each file to the Documents API

How the user and submission are identified

X-External-ID header, copied to external_id

external_id field in the API request

Processing

Asynchronous: webhook, then GET the document

Synchronous: JSON in the API response

Spam and sender filtering

Done by Veryfi

Done by you

Picking the receipt from the email

Done by Veryfi

Done by you

Retries

Done by Veryfi

Built by you

Build effort

Lower

Higher, with full control

Data flow

Email Collector data flow · forwarding, processing, webhook, fetch

The webhook only says which document is ready and its status; your system then fetches the document by ID, and external_id in that JSON tells you which user and submission it belongs to.

Roles and ownership

You own everything up to the moment the email reaches veryfi.cc and everything after the webhook arrives; Veryfi owns the processing in between.

Area

Task

Owner

Notes

Account

Provide the Veryfi account, API keys and veryfi.cc address

Veryfi

One veryfi.cc address

Account

Account settings (split attachments by default, ignore email body)

Veryfi, on your request

Ask support before go-live

Account

Webhook URL configured on the account

You

Your HTTPS endpoint; Veryfi support can help

Intake

Shared inbox on your domain (receipts@yourorg.com)

You

Intake

Publishing the address to your end users

You

App, onboarding, help center

Intake

User directory: sender email to user ID

You

Only you know who your users are

Forwarding

Match the sender to a user ID and create a submission ID

You

Forwarding

Add X-External-ID (and X-Bulk if needed)

You

Forwarding

Forward the email to veryfi.cc from your domain

You

Your mail server handles delivery retries

Processing

Spam and virus filtering

Veryfi

Processing

Choosing the receipt: body, attachments, hosted links

Veryfi

See How Veryfi reads an email

Processing

Extraction, validation and enrichment

Veryfi

Processing

Retrying temporary failures

Veryfi

Automatic; the same email never creates two documents

Processing

Returning emails it can't process

Veryfi

Sent to your forwarding inbox with the reason in the subject

Results

Receiving webhooks

You

Results

Fetching the document by ID

You

GET /api/v8/partner/documents/{id}/

Results

Saving the result to the user and submission

You

Results

Business rules (approve, flag, sync, analytics)

You

Errors

Monitoring returned emails and failed webhooks

You

Errors

Telling the end user what to resend, and resending

You

Veryfi does not reprocess a returned email

Support

Investigating a specific document or email

Veryfi

Send the document ID or send time and address

Prerequisites

Item

What you need

Where to get it

Veryfi account

An account with document allowance for your expected volume

Your Veryfi contact

veryfi.cc address

The address every email is forwarded to

Veryfi portal: COLLECT, or Settings > Email Collector

API credentials

Client ID and API key, to fetch documents after each webhook

Veryfi portal: Settings > Keys

Webhook endpoint

A public HTTPS URL on your side that accepts POST requests

You build it; register it on your Veryfi account Settings > Keys

Shared inbox

A dedicated address on your domain, not a person's mailbox

Google Workspace, Microsoft 365 or an inbound mail service

Forwarding service

A script or worker that can read new mail, add headers and send it on

You build it (example in Step 4)

User directory

Verified sender addresses mapped to a stable user ID

Your user database

Storage

A table for submissions and results

Your database


Step-by-step implementation

Eight steps take an email from your user's outbox to a receipt saved under that user.

Step 1: Prepare your Veryfi account

Owner: you, with Veryfi

  1. Copy your veryfi.cc address from Settings > Email Collector.

  2. Copy your Client ID and API key from Settings > Keys.

  3. Decide how attachments should be handled by default. If most emails hold one receipt per attachment, ask support to process attachments separately for your account. Otherwise, use the X-Bulk header per email (Step 4).

  4. Decide whether the email body should ever be processed. If your users only send attachments, ask support to ignore the email body for your account.

  5. Turn off confirmation emails if you will rely on webhooks: Settings > Profile > Auto-forward emailed receipts & bills back to me?

Step 2: Set up your shared inbox

Owner: you

  1. Create a dedicated address, for example receipts@yourorg.com.

  2. Connect it to your forwarding service. Either your mail provider pushes each new email to your service, or your service checks the mailbox every minute (Gmail API, Microsoft Graph or IMAP).

  3. Optional, since Veryfi also filters spam: turn on SPF, DKIM and DMARC checks at your mail provider.

  4. Publish the address in your app, onboarding emails and help center.

Do not use a plain auto-forward rule to veryfi.cc. Every email would arrive from your inbox, so Veryfi could not tell which user sent it.

Step 3: Identify the submission and the user

Owner: you

  1. Read the sender address, lowercase and trim it.

  2. Look it up in your user directory to get the user's ID.

  3. If there is no match, do not forward the email. Reply with instructions to register that address.

  4. Create a submission record with its own ID (for example sub-0091): user ID, sender, subject, time received and the email's Message-ID. You will use it to match results and errors later.

Common practice: accept submissions only from registered addresses. Most teams let each user submit only from the email address registered to their account, or from a short list of addresses the user has verified. Emails from any other address are not forwarded to Veryfi, and the sender gets a reply explaining how to add that address. This keeps attribution reliable and stops anyone from submitting documents on another user's behalf.

You decide how to identify users and submissions. Veryfi does not require a particular format or matching method. Choose what fits your system: which ID you use for a user, how you match a sender who uses several addresses, and what counts as one submission. Whatever you choose, keep the values stable and free of personal data, and send the same X-External-ID when you resend a submission.

Step 4: Add headers and forward to Veryfi

Owner: you

Add these headers and send the email from your own domain to your veryfi.cc address:

  • X-External-ID: the value Veryfi copies into external_id. Include both the user ID and the submission ID, for example user-48213:sub-0091, so every document traces back to the user and to the exact email they sent.

  • X-Bulk: true: only if this email's attachments should become separate documents.

This example re-sends the original email, with its body and attachments unchanged:

import smtplib
from email import policy
from email.parser import BytesParser

VERYFI_CC = "yourname@veryfi.cc" # Settings > Email Collector
FORWARDER = "receipts@yourorg.com" # your shared inbox

def forward_to_veryfi(raw_email: bytes, external_id: str, split_attachments: bool = False):
msg = BytesParser(policy=policy.default).parsebytes(raw_email)

# Send from your own domain so SPF and DKIM pass
for header in ("From", "To", "Cc", "Bcc", "DKIM-Signature", "Return-Path"):
del msg[header]
msg["From"] = FORWARDER
msg["To"] = VERYFI_CC

msg["X-External-ID"] = external_id
msg["X-Bulk"] = "true" if split_attachments else "false"

with smtplib.SMTP("smtp.yourorg.com", 587) as server:
server.starttls()
server.login(SMTP_USER, SMTP_PASSWORD)
server.send_message(msg)

Save the X-External-ID value and the send time on the submission record.

Step 5: Register your webhook

Owner: you, with Veryfi support if needed

  1. Build an HTTPS endpoint that accepts POST requests with a JSON body.

  2. Register its URL on your Veryfi account.

  3. Return HTTP 200 quickly and do the work in the background, so slow processing on your side does not cause timeouts.

Step 6: Receive the webhook and fetch the document

Owner: you

The webhook tells you which document is ready and its status. It does not contain the extracted data. Your system then requests the document by its ID and receives the full JSON.

Example notifications:

{"event": "document.created", 
"data": {"id": 251630047,
"created": "2026-11-12 15:27:26"}
}
{"event": "document.failed", 
"data": {"id": 251630048,
"created": "2026-11-12 15:29:02"}
}

Then fetch the document:

GET https://api.veryfi.com/api/v8/partner/documents/251630047/ 
CLIENT-ID: <your client id>
AUTHORIZATION: apikey <username>:<api key>
Accept: application/json

Using a Bearer key instead? Keep CLIENT-ID and send the key as a Bearer token. Bearer keys are scoped to your account and can be rotated from Settings > Keys. See Bearer keys.

GET https://api.veryfi.com/api/v8/partner/documents/251630047/ 
CLIENT-ID: <your client id>
AUTHORIZATION: Bearer <api key>
Accept: application/json
import requests
from flask import Flask, request

app = Flask(__name__)

@app.post("/veryfi/webhook")
def veryfi_webhook():
queue.enqueue(handle_event, request.get_json(force=True)) # reply fast, work in background
return "", 200

def handle_event(payload):
doc_id = payload["data"]["id"]
if payload.get("event") == "document.failed":
mark_submission_failed(doc_id)
return
resp = requests.get(
f"https://api.veryfi.com/api/v8/partner/documents/{doc_id}/",
headers={
"CLIENT-ID": CLIENT_ID,
"AUTHORIZATION": f"apikey {USERNAME}:{API_KEY}", # or f"Bearer {API_KEY}"
"Accept": "application/json",
},
timeout=60,
)
resp.raise_for_status()
doc = resp.json()
save_receipt(external_id=doc.get("external_id"), veryfi_id=doc["id"], data=doc)

The same document can be notified more than once. Save by Veryfi document id so a repeat notification updates the record instead of adding a new one.

Step 7: Save the result to the user and submission

Owner: you

  1. Read external_id from the JSON, split it into the user ID and submission ID, and match it to your submission record.

  2. Store the Veryfi document id, external_id, the extracted fields and a status

  3. Note when you split attachments (X-Bulk: true, #bulkupload or the account setting): assume one email produces several documents. They share the same external_id, each arrives with its own webhook, and all of them belong to one submission. Handle them accordingly

Step 8: Show each user their receipts

Owner: you

  1. List receipts from your database, filtered by the signed-in user's ID on the server.

  2. To reconcile a submission with Veryfi, list its documents by external ID: GET https://api.veryfi.com/api/v8/partner/documents/?external_id=user-48213:sub-0091

  3. Apply your business rules: approve, flag duplicates (is_duplicate), sync to accounting, report.

Reference

Custom headers and subject options

Option

Where

Effect

X-External-ID: <value>

Email header

Value is copied into external_id in the JSON

X-Bulk: true

Email header

Each attachment becomes its own document

#bulkupload

Subject line

Same as X-Bulk: true, for that email

#tagname or #"tag name"

Subject line

Applies that document tag, if the tag already exists in your account

Split attachments by default

Account setting (ask support)

Every email is split, without headers

Ignore email body

Account setting (ask support)

Only attachments are processed

How Veryfi reads an email

Body and attachment. Both are used and combined into one multi-page document, so the receipt is captured wherever it is. If the email has a single image attachment with almost no text (such as a signature logo), Veryfi skips the image and processes only the body.

Body only. Veryfi uses the HTML version when there is one, otherwise the plain text. If a short email body links to a receipt hosted by Square, Xero, Clover, Walmart or RockSolid, Veryfi opens the link and processes that receipt instead.

Several attachments, default mode. Attachments and body are merged into one document:

  • up to 5 attachments, largest first

  • up to 15 pages in total

  • DOCX files are converted to PDF, ZIP files are unpacked, GIF files are ignored
    ​

One PDF with several receipts or invoices. Veryfi reads each PDF as one document, with one vendor, one set of totals and one list of line items. If a user attaches a single PDF that bundles several invoices, the results can blend, for example one invoice's total showing up as a line item on another. Split mode separates attachments, not the pages inside one PDF, and only the first 15 pages of a PDF are processed by default. Ask users to attach one PDF per receipt or invoice. For bundles you can't avoid, split the PDF on your side, or send it through the API to the Smart PDF Splitter (POST /api/v8/partner/documents-set), which detects each document and returns one extraction per document. See Multipage PDFs with multiple documents.

Two common cases.

What the user sends

Default mode (attachments merged)

Split mode (X-Bulk: true)

What to do

Several separate receipts, each as its own attachment

All attachments are merged into one PDF and processed as one document: one vendor, one date, one total. Data from different receipts can blend, for example one receipt's total appearing as a line item of another

Each attachment becomes its own document, with its own webhook and the same external_id

Use split mode whenever users may send several receipts in one email

One PDF that holds several receipts, such as an expense report with scanned receipts

Processed as one document. Totals and line items from the different receipts can blend, or the report's summary page can be read as the receipt

Still one document: split mode separates attachments, not the pages inside a PDF

Ask users to attach each receipt separately. For bundles you can't avoid, split the PDF before forwarding, or send it through the API to the Smart PDF Splitter

Several attachments, split mode (X-Bulk: true, #bulkupload or the account setting):

  • each attachment becomes its own document, with one webhook each

  • no limit on the number of attachments

  • up to 10 pages per document; attachments over the size limit are skipped

  • the email body is not processed

Optional: email details in the JSON

The JSON has no dedicated fields for the email's subject or addresses. To keep one of them, create a Data Transformation rule in the Veryfi portal (Data Transformation > Rules) that copies it into the notes field, for example {EmailedReceipt.subject}. Other placeholders: {EmailedReceipt.email_from}, {EmailedReceipt.email_to}, {EmailedReceipt.email_cc}. With this option, email_from is your forwarding address, so use X-External-ID to identify the user and submission.

Error handling

Veryfi retries temporary failures for you, so you don't build retry logic. You do need to watch for emails Veryfi can't process, tell the user, and resend once the cause is fixed.

What Veryfi handles

  • Temporary processing failures are retried automatically.

  • The same email never creates two documents.

  • Delivery to veryfi.cc is retried by your mail server, as with any email.

What you handle

1. Returned emails. If Veryfi can't process an email, it sends it back to your forwarding inbox (receipts@yourorg.com) with the reason in the subject line. Route these to your error handling and match each one to its submission record.

Reason

What to do

No file or receipt found

Ask the user to resend with the receipt attached, or as an e-receipt in the body

Not a receipt or invoice

No action, unless the user disagrees

Unsupported, too large or password-protected file

Ask for a PDF or image within the limits, or an unlocked copy

Document allowance reached

Contact Veryfi to raise the limit, then resend. These emails are not processed automatically once the limit is raised

The reasons match the API error responses.

  1. Failed webhooks. A document.failed event means that document could not be processed. Mark the submission as failed and follow the same steps as for a returned email.

  2. No response. If a submission gets neither a webhook nor a returned email within 30 minutes, check spam and junk folders, then contact support@veryfi.com with the send time, your forwarding address and the X-External-ID value.

  3. Resending. Veryfi does not reprocess a returned email. Once the cause is fixed, forward the email again with the same X-External-ID. Each resend creates a new document, so resend only what failed.

  4. Your webhook endpoint. If your endpoint is down, you may miss notifications. Run a daily check that lists your recent documents and compares them with your submission records, so nothing is lost.

Testing and go-live checklist

Run every test with at least two test users before you publish the address.

  • A single PDF lands under the right user and submission, with the external_id you sent

  • An e-receipt in the email body (no attachment) is extracted

  • An email with a signature logo and an HTML receipt is extracted from the body

  • Three attachments with X-Bulk: true become three documents, three webhooks, same external_id

  • Three attachments without X-Bulk become one document

  • An email from an unregistered sender is not forwarded and gets the registration reply ( optional)

  • A password-protected PDF comes back to your forwarding inbox with the reason, and the user is told

  • A document.failed webhook marks the submission as failed

  • The webhook endpoint answers 200 within a few seconds and fetches the document in the background

  • A repeated webhook for the same document does not create a duplicate record

  • User A cannot see User B's receipts in any view

  • A submission with no result after 30 minutes raises an alert

Support: contact support@veryfi.com. Include the Veryfi document id and your external_id, or for an email with no result, the send time and your forwarding address.

Did this answer your question?