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 |
|
|
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
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 | 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 |
|
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
Copy your veryfi.cc address from Settings > Email Collector.
Copy your Client ID and API key from Settings > Keys.
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-Bulkheader per email (Step 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.
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
Create a dedicated address, for example receipts@yourorg.com.
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).
Optional, since Veryfi also filters spam: turn on SPF, DKIM and DMARC checks at your mail provider.
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
Read the sender address, lowercase and trim it.
Look it up in your user directory to get the user's ID.
If there is no match, do not forward the email. Reply with instructions to register that address.
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 intoexternal_id. Include both the user ID and the submission ID, for exampleuser-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
Build an HTTPS endpoint that accepts POST requests with a JSON body.
Register its URL on your Veryfi account.
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
Read
external_idfrom the JSON, split it into the user ID and submission ID, and match it to your submission record.Store the Veryfi document
id,external_id, the extracted fields and a statusNote 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
List receipts from your database, filtered by the signed-in user's ID on the server.
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-0091Apply your business rules: approve, flag duplicates (
is_duplicate), sync to accounting, report.
Reference
Custom headers and subject options
Option | Where | Effect |
| Email header | Value is copied into |
| Email header | Each attachment becomes its own document |
| Subject line | Same as |
| 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 ( | 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 | 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.
Failed webhooks. A
document.failedevent means that document could not be processed. Mark the submission as failed and follow the same steps as for a returned email.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-IDvalue.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.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_idyou sentAn 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: truebecome three documents, three webhooks, sameexternal_idThree attachments without
X-Bulkbecome one documentAn 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.failedwebhook marks the submission as failedThe 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.
