Asynchronous Processing
Before we get into the details of Asynchronous processing, please note that this option is only available for the Receipts / Invoices OCR API API Docs https://api.veryfi.com/api/v8/partner/documents
Please refer to API docs for the most up-to-date information.
Synchronous vs. Asynchronous Document Processing
Synchronous Processing: Best When
Immediate Results Needed: User is actively waiting for document data (e.g., mobile app scanning receipts for instant expense reporting)
Sequential Workflows: Next steps depend directly on document extraction results
Simple Documents: Processing is quick and straightforward (business cards, short forms)
Low Volume: Handling individual documents rather than batches
Interactive Applications: Real-time user experiences where feedback is immediate
Asynchronous Processing: Best When
High Volume: Processing large batches of documents (e.g., month-end invoice processing)
Complex Documents: Documents require extensive processing time (multi-page contracts, complex tables)
Background Processing: Results can be handled later without blocking other operations
Resource Management: Need to control system load and prevent timeout issues
Webhook Integration: Systems that operate on event-based architectures
The choice typically depends on response time requirements, document complexity, and processing volume.
Asynchronous Processing with Veryfi
Process documents in the background when immediate results aren't needed:
Submit document with
{"async": true}Veryfi processes and notifies your pre-configured webhook URL
Your service requests the complete extraction results via API
Ideal for high-volume processing, complex documents, or when managing system resources is critical. Ensures your application remains responsive while handling document processing in the background.
How can one Add & Confirm a webhook?
Programatically (API-Based Webhook Management)
Use Veryfi Web Portal GUI
1. Programatically (API-Based Webhook Management)
API capability for webhook management allows programmatic control of webhook configurations. This enhancement addresses previous limitations where webhook URLs could only be configured through the web portal.
Key Benefits
Seamlessly integrate webhook configuration into CI/CD pipelines
Eliminate manual portal interventions during automated deployments
Configure webhooks at scale across multiple environments
Ensure compatibility with custom security requirements (e.g., AWS API Gateway signature headers)
Available Operations
The new API endpoints enable you to:
Retrieve existing webhook configurations
Add new webhook URLs
Confirm webhook functionality
Please refer to API Documentation
Testing example:
Prerequisites:
Environments and tools:
Python 3.10+ - Installed and working in your environment (Local, Uv, Canda, etc)
Uvicorn installed (Installation - Uvicorn )
Ngrok installed (Download ngrok )
Set up the fast API app.
Create an small receiver (webhook_receiver.py) file with a “ /webhook “ endpoint that logs incoming requests and return 200 ok.
This acts as you public listener, you are exposing an url to the network.
App Example
fromfastapi import FastAPI, Request import logging, sys app = FastAPI() logging.basicConfig(stream=sys.stdout, level=logging.INFO) @app.post("/webhook") # this could as well be @app.post("/") for root. async def webhook(req: Request): headers = dict(req.headers) body = await req.body() logging.info("=== Webhook delivery ===") logging.info("Headers: %s", headers) logging.info("Body: %s", body.decode(errors="ignore")) return {"ok": True}
This app should be saved as a .py file that can be launched using uvicorn. webhook_receiver.py) in this example
Launch the app locally with uvicorn.
From terminal:
uvicorn webhook_receiver.py:app --port 8000 #This could be any other port as long as it matches next stepThis starts the app on https://127.0.0.1:8000 (or another specified port.)
Here it is only accesible from your own machine now (locally).
This will launch the process on your current terminal, leave it active.
Expose the app to public network.
At this point you already have a public webhook to test any async load that you might need. It is only missing the registration from the UI/API. (Described below)
From here and below, we will explore the webhook management options
Only ONE webhook can be active at the time, either created from the UI or from the API.
API CALLS TESTING
Create or Update (same process):
To register a webhook you will perform a POST on the veryfi endpoint to “write” your url, this goes a raw data in the call body:
example using curl:
Client ID and key are already provided if you copy the call from the docscurl -L -X POST 'https://api.veryfi.com/api/v8/partner/settings/webhooks' \ -H 'Content-Type: application/json' \ -H 'CLIENT-ID: client id goes here' \ #Replace using your client ID -H 'AUTHORIZATION: apikey user:key goew here' \ # Replace using your key --data-raw '{ "url": "ngrok-url-example.dev/webhook" #your url here. (use "/webhook" at the end if you are using the same app I provided earlier) }'This will register the webhook, but a confirmation is still pending. In order to confirm, go back to terminal, and look for the uvicorn process running.
In this uvicorn process window you will see the app response including a secret close to the end of the body. copy this secret.
Confirm the webhook.
With the previous generated secret, you can now register the webhook.
curl -L -X POST 'https://api.veryfi.com/api/v8/partner/settings/webhooks/confirm' \ -H 'Content-Type: application/json' \ -H 'CLIENT-ID: client id goes here' \ #Replace using your client ID -H 'AUTHORIZATION: apikey user:key goew here' \ # Replace using your key --data-raw '{ "url": "ngrok-url-example.dev/webhook" #your url here. (use "/webhook" at the end if you are using the same app I provided earlier) "secret": "REPLACE THE SeCRET" # Replace the secret generated in uvicorn. }'Once the call is completed you will get a 200 response. The webhook is ready for use.
You can also use your ngrok process to confirm the server response.
This can be confirmed by going to Setting > keys > Webhooks, if you were already there just refresh.
2. Use Veryfi Web Portal GUI
Veryfi Web Portal: https://app.veryfi.com
Checks out Veryfi OCR API Portal Walk-Through
Webhook URL validation
A verification step has been added to our web portal in order to prevent private/invalid URLs.
When you change the Webhook URL in the settings/keys, we run a verification process:
A subscription event is submitted in a POST request to his Webhook URL with a secret payload.
Payload should contain a client-specific secret:{ "event": "subscription", "data": { "secret": "awdca8efquanvc8ahcwncw, "created": "2023-05-15 00:00:00" } }A confirmation modal is shown, where the user has to enter the secret value. If it matches, the webhook URL will saved.
If an error occurs a modal will shown.
Restrictions:
You can't save without changes.
You can't use Veryfi's domain (veryfi.com).
https://webhook.site/'is not allowed.You can save it empty in order to clean any pre saved URL.
You can only use HTTPS://
The link should be a public link.
Step 1: Submit a document for processing
Submit a document for processing using one of the methods described in the synchronous processing section inside your account API documentation.
Make sure that you enable asynchronous processing mode by adding this input parameter to the JSON request body:
{ "async": true }
The response will contain the document ID assigned to the document that is being processed. No additional information is available at this point as the document has not yet been processed.
{ "id": 123456789 }
Step 2: Receiving and validating the webhook request
Within seconds of submitting a document for processing, you will receive a webhook POST request to your specified webhook URL. The request body will be in JSON format and look similar to this:
Success Webhook Post Request
}
"event": "document.created",
"data": {
"id": 123456789,
"created": "2021-10-20 15:27:26"
}
}
Failure Webhook Post Request
{
"event": "document.failed",
"data": {
"id": 123456789,
"created": "2021-10-20 15:27:26"
}
}
Please note, that after September 2023
Failure Webhook Post Request will be updated to
{
"event": "document.failed",
"data": {
"id": 123456789,
"created": "2023-08-10 19:11:32",
"error”: "<reason for failure>"
}
}
In order to validate that this request was sent by Veryfi, a special validation header will be included in this request:
"x-veryfi-signature": "3awRveqxsYK5nB6Yjca4oq1ToV77ksq+sZ34ab7YoEq="
To validate the request, you will need 3 things:
payload- this is the entire JSON request bodyclient_secret- this can be found inside your Veryfi account herevalidation_signature- this is the value of the x-veryfi-signature header in the request
The example code below demonstrates how to validate the incoming request.
Python
import hashlib
import hmac
import base64
from typing import *
def create_signature(data_payload: Dict, client_secret: str) -> str:
signature = hmac.new(
client_secret.encode("utf-8"), msg=str(data_payload).encode("utf-8"), digestmod=hashlib.sha256
).digest()
base64_signature = str(base64.b64encode(signature), "utf-8").strip()
return base64_signature
generated_signature = create_signature(payload["data"], client_secret)
# Confirm that the generated signature equals the validation signature
# from the x-veryfi-signature header
is_valid = generated_signature == validation_signature
JavaScript
function createSignature(payload, clientSecret) {
return crypto
.createHmac("sha256", clientSecret)
.update(
JSON.stringify(payload.data)
.replace(/,/g, ", ")
.replace(/":/g, '": ')
.replace(/"/g, "'")
)
.digest("base64");
}
const generatedSignature = createSignature(payload, clientSecret);
const isValid = generatedSignature === validationSignature;
import hashlib import hmac import base64 from typing import * def create_signature(data_payload: Dict, client_secret: str) -> str: signature = hmac.new( client_secret.encode("utf-8"), msg=str(data_payload).encode("utf-8"), digestmod=hashlib.sha256 ).digest() base64_signature = str(base64.b64encode(signature), "utf-8").strip() return base64_signature generated_signature = create_signature(payload["data"], client_secret) # Confirm that the generated signature equals the validation signature # from the x-veryfi-signature header is_valid = generated_signature == validation_signature
Add webhook URL in the settings.
The URL should be an endpoint that accepts POST requests with JSON data and returns HTTP 200 response after receiving the request
Once document processing is complete, you will receive a similar request to your webhook:
curl --location --request POST 'https://webhook_address/webhook/veryfi' \ --header 'Content-Type: application/json' \ --data-raw '{"id": 41331192, "created": "2021-09-19 20:37:47" }'
{ "timestamp": "2021-09-20T18:54:13.375+00:00", "status": 400, "error": "Bad Request", "message": "", "path": "/v1/check/webhook/veryfi" }
Step 3: Retrieve the processed document
Now that the webhook request has been validated as indeed coming from Veryfi, the next step is to retrieve the data extracted from the document and perform any additional required tasks.
To get the document's details, perform a GET request /api/v8/partner/documents/DOCUMENT_ID/ as described inside your Veryfi account here. DOCUMENT_ID can be found by accessing data.id in the JSON request body for the webhook request in step 2.
NOTES:
Only admins have permission to set up webhook URLs
Dev profiles aka “sandbox“ have separate api keys, hence can have a different webhook URL in the settings
Read more about My Team and Permissions
Email Collector Webhook Integration
When using Email Collector with a configured webhook URL, Veryfi automatically processes emails asynchronously and notifies your webhook upon completion.
If processing succeeds, retrieve the document data via: /api/v8/partner/documents/DOCUMENT_ID/
If processing fails, your webhook receives:
{
"event": "document.failed",
"data": {
"id": 123456789,
"created": "2021-10-20 15:27:26"
}
}
Webhook Error Handling
When a document is processed, Veryfi sends a notification to your webhook URL (configured in Settings/Keys).
Retry Mechanism:
Timeouts: 3 seconds for connection, 20 seconds for response
Any response code below 400 is considered successful
Failed deliveries trigger 2 additional attempts with increasing intervals
After 3 failed attempts, the system stops retrying
Failure Notification:
One daily summary email sent to the company account owner
Email contains all document IDs with failed webhook deliveries within the past 24 hours
This ensures you're aware of any webhook communication issues without flooding your inbox.
Refer to API Docs for more information
If you experience any problems or need help setting this up, please reach out to [email protected], we are here for you.





