This guide covers how to verify business documents using the Parcha API. We’ll use incorporation document verification as our canonical example to demonstrate the three main approaches.
Choosing the Right Approach
Parcha offers three ways to verify documents, each optimized for different use cases:
Quick Comparison
Approach Endpoint Speed Use Case 1. Full Agent Job startKYBAgentJobVaries Document verification as part of comprehensive KYB review with multiple checks 2. Prevalidation runCheckFast Quick document type validation before full review 3. Full Verification runCheckThorough Complete verification with visual analysis and fraud detection
Incorporation Document Example
We’ll demonstrate all three approaches using incorporation document verification. The same patterns apply to other document types (proof of address, EIN documents, etc.).
Sample Business Data
business_data = {
"registered_business_name" : "Example Business Inc." ,
"incorporation_date" : "2023-01-01" ,
"address_of_incorporation" : {
"street_1" : "123 Main St" ,
"city" : "Wilmington" ,
"state" : "DE" ,
"country_code" : "US" ,
"postal_code" : "19801"
}
}
Approach 1: Full Agent Job
Best for: Comprehensive compliance reviews Use Agent Jobs when you need to run multiple checks together as part of a unified compliance workflow with custom pass/fail criteria.
When to use:
Running a full KYB review with multiple checks (web presence + sanctions + document verification)
You have an agent configured with specific pass/fail logic
You need unified case view and risk scoring across all checks
How it works:
curl -X POST 'https://api.parcha.ai/api/v1/startKYBAgentJob' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"agent_key": "your-kyb-agent-key",
"kyb_schema": {
"id": "case-001",
"self_attested_data": {
"registered_business_name": "Example Business Inc.",
"website": "https://example.com",
"incorporation_date": "2023-01-01",
"address_of_incorporation": {
"street_1": "123 Main St",
"city": "Wilmington",
"state": "DE",
"country_code": "US",
"postal_code": "19801"
},
"incorporation_documents": [
{
"file_name": "certificate_of_incorporation.pdf",
"b64_document": "BASE64_ENCODED_DOCUMENT"
}
]
}
},
"webhook_url": "https://your-webhook.com/callback"
}'
import requests
import base64
api_key = 'YOUR_API_KEY'
url = 'https://api.parcha.ai/api/v1/startKYBAgentJob'
with open ( 'certificate_of_incorporation.pdf' , 'rb' ) as file :
b64_document = base64.b64encode( file .read()).decode( 'utf-8' )
data = {
"agent_key" : "your-kyb-agent-key" ,
"kyb_schema" : {
"id" : "case-001" ,
"self_attested_data" : {
"registered_business_name" : "Example Business Inc." ,
"website" : "https://example.com" ,
"incorporation_date" : "2023-01-01" ,
"address_of_incorporation" : {
"street_1" : "123 Main St" ,
"city" : "Wilmington" ,
"state" : "DE" ,
"country_code" : "US" ,
"postal_code" : "19801"
},
"incorporation_documents" : [
{
"file_name" : "certificate_of_incorporation.pdf" ,
"b64_document" : b64_document
}
]
}
},
"webhook_url" : "https://your-webhook.com/callback"
}
response = requests.post(url, json = data, headers = {
'Authorization' : f 'Bearer { api_key } ' ,
'Content-Type' : 'application/json'
})
job_id = response.json()[ 'job_id' ]
print ( f "Agent job started: { job_id } " )
Agent configuration matters: Your agent key determines which checks run and the pass/fail criteria. Work with Parcha to configure agents that match your compliance policies.
Approach 2: Prevalidation (Quick Check)
Best for: Real-time document upload validation Use Prevalidation for fast document type checking without running full verification. Perfect for inline validation during user uploads.
When to use:
Real-time document upload flows where users expect quick feedback
Pre-screening documents before committing to full verification
Validating document type before user submits their application
How it works:
Set all verification flags to false to skip visual verification and fraud checks. The system will only validate that the document is a valid type. Use a webhook to receive the result quickly.
curl -X POST 'https://api.parcha.ai/api/v1/runCheck' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"agent_key": "your-agent-key",
"check_id": "kyb.incorporation_document_verification",
"kyb_schema": {
"id": "case-001",
"self_attested_data": {
"registered_business_name": "Example Business Inc.",
"incorporation_date": "2023-01-01",
"incorporation_documents": [
{
"file_name": "certificate_of_incorporation.pdf",
"b64_document": "BASE64_ENCODED_DOCUMENT"
}
]
}
},
"check_args": {
"enable_visual_verification": false,
"enable_fraud_check": false,
"verify_incorporation_date": false,
"verify_registration_number": false,
"verify_address": false,
"verify_no_high_risk_fraud": false
},
"webhook_url": "https://your-webhook.com/prevalidation-callback"
}'
import requests
import base64
api_key = 'YOUR_API_KEY'
url = 'https://api.parcha.ai/api/v1/runCheck'
with open ( 'certificate_of_incorporation.pdf' , 'rb' ) as file :
b64_document = base64.b64encode( file .read()).decode( 'utf-8' )
# Prevalidation mode - all verification flags disabled
data = {
"agent_key" : "your-agent-key" ,
"check_id" : "kyb.incorporation_document_verification" ,
"kyb_schema" : {
"id" : "case-001" ,
"self_attested_data" : {
"registered_business_name" : "Example Business Inc." ,
"incorporation_date" : "2023-01-01" ,
"incorporation_documents" : [
{
"file_name" : "certificate_of_incorporation.pdf" ,
"b64_document" : b64_document
}
]
}
},
"check_args" : {
"enable_visual_verification" : False ,
"enable_fraud_check" : False ,
"verify_incorporation_date" : False ,
"verify_registration_number" : False ,
"verify_address" : False ,
"verify_no_high_risk_fraud" : False
},
"webhook_url" : "https://your-webhook.com/prevalidation-callback"
}
response = requests.post(url, json = data, headers = {
'Authorization' : f 'Bearer { api_key } ' ,
'Content-Type' : 'application/json'
})
job_id = response.json()[ 'id' ]
print ( f "Prevalidation job started: { job_id } " )
Webhook or Poll? Provide a webhook_url (recommended) to receive results automatically as soon as prevalidation completes, or poll the /getJobById endpoint if webhooks aren’t feasible for your setup.
Approach 3: Full Verification
Best for: Thorough document analysis Use Full Verification for complete document analysis including visual verification, fraud detection, and data extraction.
When to use:
Final verification before approving a business application
When you need visual element analysis (seals, signatures)
When fraud detection is required
When you need to verify specific data fields (dates, addresses)
How it works:
Enable the verification options you need. Each adds additional analysis capabilities:
curl -X POST 'https://api.parcha.ai/api/v1/runCheck' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"agent_key": "your-agent-key",
"check_id": "kyb.incorporation_document_verification",
"kyb_schema": {
"id": "case-001",
"self_attested_data": {
"registered_business_name": "Example Business Inc.",
"incorporation_date": "2023-01-01",
"address_of_incorporation": {
"street_1": "123 Main St",
"city": "Wilmington",
"state": "DE",
"country_code": "US",
"postal_code": "19801"
},
"incorporation_documents": [
{
"file_name": "certificate_of_incorporation.pdf",
"b64_document": "BASE64_ENCODED_DOCUMENT"
}
]
}
},
"check_args": {
"enable_visual_verification": true,
"enable_fraud_check": true,
"verify_incorporation_date": true,
"verify_registration_number": true,
"verify_address": true,
"verify_no_high_risk_fraud": true
},
"webhook_url": "https://your-webhook.com/callback"
}'
import requests
import base64
api_key = 'YOUR_API_KEY'
url = 'https://api.parcha.ai/api/v1/runCheck'
with open ( 'certificate_of_incorporation.pdf' , 'rb' ) as file :
b64_document = base64.b64encode( file .read()).decode( 'utf-8' )
# Full verification mode - all checks enabled
data = {
"agent_key" : "your-agent-key" ,
"check_id" : "kyb.incorporation_document_verification" ,
"kyb_schema" : {
"id" : "case-001" ,
"self_attested_data" : {
"registered_business_name" : "Example Business Inc." ,
"incorporation_date" : "2023-01-01" ,
"address_of_incorporation" : {
"street_1" : "123 Main St" ,
"city" : "Wilmington" ,
"state" : "DE" ,
"country_code" : "US" ,
"postal_code" : "19801"
},
"incorporation_documents" : [
{
"file_name" : "certificate_of_incorporation.pdf" ,
"b64_document" : b64_document
}
]
}
},
"check_args" : {
"enable_visual_verification" : True ,
"enable_fraud_check" : True ,
"verify_incorporation_date" : True ,
"verify_registration_number" : True ,
"verify_address" : True ,
"verify_no_high_risk_fraud" : True
},
"webhook_url" : "https://your-webhook.com/callback"
}
response = requests.post(url, json = data, headers = {
'Authorization' : f 'Bearer { api_key } ' ,
'Content-Type' : 'application/json'
})
job_id = response.json()[ 'id' ]
print ( f "Full verification job started: { job_id } " )
Verification Options Reference
Latency vs. Thoroughness Trade-off: Each verification option adds scrutiny but increases processing time. Choose based on your risk tolerance and UX requirements.
Option Default Description Latency Impact enable_visual_verificationtrueUses vision model to analyze seals, signatures, visual elements +15-30 seconds enable_fraud_checkfalseSophisticated fraud and tampering analysis on document metadata +1-2 minutes verify_incorporation_datetrueFail if incorporation date has significant discrepancies Minimal verify_registration_numberfalseFail if registration number has significant discrepancies Minimal verify_addressfalseFail if address has significant discrepancies Minimal verify_no_high_risk_fraudfalseFail if HIGH_RISK fraud indicators detected Minimal
By default (without explicit options), the system performs OCR-based analysis using LLMs to extract and analyze text from the document.
Polling for Results
After starting a job, poll for completion:
curl 'https://api.parcha.ai/api/v1/getJobById?job_id=YOUR_JOB_ID&include_check_results=true' \
-H 'Authorization: Bearer YOUR_API_KEY'
import requests
import time
api_key = 'YOUR_API_KEY'
job_id = 'YOUR_JOB_ID'
while True :
response = requests.get(
'https://api.parcha.ai/api/v1/getJobById' ,
headers = { 'Authorization' : f 'Bearer { api_key } ' },
params = { 'job_id' : job_id, 'include_check_results' : 'true' }
)
job_data = response.json()
if job_data[ 'status' ].lower() == 'complete' :
check_result = job_data[ 'check_results' ][ 0 ]
if check_result[ 'passed' ]:
print ( "✓ Document verified successfully" )
payload = check_result[ 'payload' ]
print ( f " Company: { payload.get( 'company_name' ) } " )
print ( f " Incorporation Date: { payload.get( 'incorporation_date' ) } " )
else :
print ( f "✗ Verification failed: { check_result.get( 'answer' ) } " )
break
elif job_data[ 'status' ].lower() == 'failed' :
print ( "Job failed!" )
break
time.sleep( 5 )
Using Webhooks
Instead of polling, configure a webhook to receive results automatically:
const express = require ( 'express' );
const app = express ();
app . use ( express . json ());
app . post ( '/callback' , ( req , res ) => {
const jobData = req . body ;
if ( jobData . status === 'complete' ) {
const incorpCheck = jobData . check_results . find (
check => check . command_id === 'kyb.incorporation_document_verification'
);
if ( incorpCheck ) {
if ( incorpCheck . passed ) {
console . log ( '✓ Document verified' );
console . log ( 'Company:' , incorpCheck . payload . company_name );
} else {
console . log ( '✗ Verification failed:' , incorpCheck . answer );
}
}
}
res . sendStatus ( 200 );
});
app . listen ( 3000 );
For more details, see the Webhooks documentation .
Decision Guide
I need to run a full compliance review with multiple checks
Use: Approach 1 - Full Agent Job (startKYBAgentJob) Include documents in your kyb_schema and let the agent handle document verification alongside web presence, sanctions screening, and other checks. You get unified pass/fail logic across all verification types.
I need quick validation during document upload
Use: Approach 2 - Prevalidation (runCheck with flags disabled) Set all verification flags to false for fast document type validation. Show users immediate feedback on whether their document is the right type before they submit.
I need thorough verification with fraud detection
Use: Approach 3 - Full Verification (runCheck with flags enabled) Enable enable_visual_verification and enable_fraud_check for comprehensive analysis. Use this for final verification before approving applications.
I want quick prevalidation followed by full verification
Combine Approaches 2 and 3
Run prevalidation during upload for quick feedback
Run full verification when user submits their complete application
This gives users fast feedback while ensuring thorough verification before approval.
Document Upload Options
When submitting documents for verification, you have two options for how to include the document data:
Option A: Inline Base64 (Single Request)
Include the base64-encoded document directly in your verification request. This is the simplest approach for most use cases.
How it works:
When to use:
Most common approach
Documents under 10MB
When you want simplicity
import base64
with open ( 'document.pdf' , 'rb' ) as file :
b64_document = base64.b64encode( file .read()).decode( 'utf-8' )
data = {
"agent_key" : "your-agent-key" ,
"check_id" : "kyb.incorporation_document_verification" ,
"kyb_schema" : {
"id" : "case-001" ,
"self_attested_data" : {
"registered_business_name" : "Example Business Inc." ,
"incorporation_documents" : [
{
"file_name" : "certificate.pdf" ,
"b64_document" : b64_document # Document included inline
}
]
}
},
"check_args" : { ... }
}
Option B: Pre-upload (Two-Step)
Upload the document first to get a URL, then reference that URL in your verification request. Useful when you need to manage documents separately.
How it works:
When to use:
Large documents
When you need to reuse the same document across multiple checks
When you want to manage document storage separately
Step 1: Upload the document
curl -X POST 'https://api.parcha.ai/api/v1/uploadB64Document' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"b64_document": "BASE64_ENCODED_CONTENT",
"document_name": "certificate_of_incorporation.pdf"
}'
import requests
import base64
api_key = 'YOUR_API_KEY'
with open ( 'certificate_of_incorporation.pdf' , 'rb' ) as file :
b64_document = base64.b64encode( file .read()).decode( 'utf-8' )
response = requests.post(
'https://api.parcha.ai/api/v1/uploadB64Document' ,
headers = {
'Authorization' : f 'Bearer { api_key } ' ,
'Content-Type' : 'application/json'
},
json = {
'b64_document' : b64_document,
'document_name' : 'certificate_of_incorporation.pdf'
}
)
document_url = response.json()[ 'url' ]
print ( f "Document uploaded: { document_url } " )
Step 2: Run verification with the URL
data = {
"agent_key" : "your-agent-key" ,
"check_id" : "kyb.incorporation_document_verification" ,
"kyb_schema" : {
"id" : "case-001" ,
"self_attested_data" : {
"registered_business_name" : "Example Business Inc." ,
"incorporation_documents" : [
{
"file_name" : "certificate_of_incorporation.pdf" ,
"url" : document_url # Reference the uploaded document
}
]
}
},
"check_args" : { ... }
}
response = requests.post(
'https://api.parcha.ai/api/v1/runCheck' ,
headers = { 'Authorization' : f 'Bearer { api_key } ' , 'Content-Type' : 'application/json' },
json = data
)
Comparison
Option Requests Best For A: Inline Base64 1 Simple integration, most use cases B: Pre-upload 2 Large files, document reuse, separate storage management
Other Document Types
The same three approaches work for all document verification checks:
Document Type Check ID Document Field Incorporation Certificate kyb.incorporation_document_verificationincorporation_documentsProof of Address kyb.proof_of_address_verificationproof_of_address_documentsEIN Document kyb.ein_document_verificationein_documentsBusiness Ownership kyb.business_ownership_document_verificationbusiness_ownership_documentsSource of Funds kyb.source_of_funds_document_verificationsource_of_funds_documents
Simply swap the check_id and document field in your requests.