Skip to main content

Address Verification

Address verification confirms that an applicant resides at their stated address. This is a common regulatory requirement for KYC compliance, helping prevent fraud and meet anti-money laundering obligations.

Verification Methods

TrustGate supports multiple address verification methods:

MethodDescriptionUse Case
Document VerificationUtility bills, bank statementsStandard KYC
Database LookupCredit bureau/postal dataQuick verification
Manual ReviewHuman review of documentsComplex cases

Accepted Documents

Primary Documents

Document TypeAccepted AgeRequired Information
Utility Bill3 monthsName, address, date
Bank Statement3 monthsName, address, date
Credit Card Statement3 monthsName, address, date
Government Letter12 monthsName, address, date
Tax Document12 monthsName, address, tax year

Secondary Documents

Document TypeAccepted AgeNotes
Rental Agreement12 monthsMust show current address
Insurance Policy6 monthsHome/auto insurance
Mortgage Statement6 monthsProperty must match address
Council Tax Bill12 monthsUK specific

Document Upload

Via API

curl -X POST https://api.bytrustgate.com/v1/documents \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: multipart/form-data" \
-F "applicant_id=550e8400-e29b-41d4-a716-446655440000" \
-F "type=utility_bill" \
-F "file=@/path/to/utility_bill.pdf"

Python Example

import requests
from datetime import datetime, timedelta

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.bytrustgate.com/v1"

# Upload proof of address document
with open("utility_bill.pdf", "rb") as f:
response = requests.post(
f"{BASE_URL}/documents",
headers={"Authorization": f"Bearer {API_KEY}"},
data={
"applicant_id": applicant_id,
"type": "utility_bill",
},
files={"file": f},
)

document = response.json()
print(f"Document status: {document['status']}")

# Check extracted address
if document["status"] == "verified":
extracted_address = document["extracted_data"]["address"]
print(f"Extracted address: {extracted_address}")

OCR Extraction Results

Utility Bill Example

{
"id": "doc_456789",
"type": "utility_bill",
"status": "verified",
"extracted_data": {
"document_type": "utility_bill",
"provider": "Pacific Gas & Electric",
"utility_type": "electricity",
"account_holder": "JOHN M. DOE",
"address": {
"line1": "123 Main Street",
"line2": "Apt 4B",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "USA"
},
"statement_date": "2025-01-01",
"due_date": "2025-01-25",
"amount_due": 125.43
},
"confidence_scores": {
"overall": 0.93,
"name": 0.96,
"address": 0.91,
"date": 0.95
}
}

Bank Statement Example

{
"id": "doc_567890",
"type": "bank_statement",
"status": "verified",
"extracted_data": {
"document_type": "bank_statement",
"institution": "Chase Bank",
"account_holder": "JOHN MICHAEL DOE",
"address": {
"line1": "123 Main Street",
"line2": "Apt 4B",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "USA"
},
"statement_period": {
"start": "2024-12-01",
"end": "2024-12-31"
},
"account_type": "checking"
}
}

Address Matching

TrustGate automatically compares extracted addresses with applicant-provided addresses.

Match Logic

The system performs fuzzy matching to handle:

  • Abbreviations (St./Street, Ave./Avenue)
  • Minor spelling variations
  • Missing/extra unit numbers
  • Postal code format differences

Match Results

{
"address_verification": {
"status": "matched",
"confidence": 0.95,
"applicant_address": {
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102"
},
"document_address": {
"line1": "123 Main Street",
"line2": "Apt 4B",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102"
},
"differences": [
{
"field": "line1",
"applicant": "123 Main St",
"document": "123 Main Street",
"similarity": 0.92,
"note": "Abbreviation difference"
},
{
"field": "line2",
"applicant": null,
"document": "Apt 4B",
"similarity": 0.0,
"note": "Unit number in document only"
}
]
}
}

Match Status Values

StatusDescriptionAction
matchedAddresses match within toleranceAuto-pass
partial_matchSome fields match, differences notedReview recommended
mismatchAddresses don't matchManual review required
unverifiableCannot extract address from documentRequest new document

Document Age Validation

Documents must be recent to be valid:

from datetime import datetime, timedelta

def validate_document_age(document, max_age_months=3):
"""Check if proof of address document is within acceptable age."""
statement_date = document["extracted_data"].get("statement_date")

if not statement_date:
return False, "No date found on document"

doc_date = datetime.fromisoformat(statement_date)
max_age = timedelta(days=max_age_months * 30)

if datetime.now() - doc_date > max_age:
return False, f"Document is older than {max_age_months} months"

return True, "Document age is acceptable"

Handling International Addresses

Address Format by Country

TrustGate normalizes addresses based on country:

United States:

123 Main Street, Apt 4B
San Francisco, CA 94102

United Kingdom:

Flat 2, 45 High Street
London
SW1A 1AA

Germany:

Hauptstraße 123
10115 Berlin

Country-Specific Fields

CountrySpecial Fields
USAState (2-letter), ZIP code, ZIP+4
UKPostcode, County (optional)
GermanyPLZ (postal code)
JapanPrefecture, ward/district
AustraliaState (3-letter), postcode

Configuration Options

Address Verification Settings

Configure in Settings > Workflows:

SettingDefaultDescription
require_address_verificationtrueMake address proof mandatory
max_document_age_months3Maximum age for proof documents
address_match_threshold0.85Minimum similarity for auto-pass
accepted_document_typesallLimit accepted document types

Country-Specific Rules

{
"address_verification_rules": {
"USA": {
"max_document_age_months": 3,
"require_state": true,
"validate_zip": true
},
"GBR": {
"max_document_age_months": 3,
"require_postcode": true,
"accept_council_tax": true
},
"DEU": {
"max_document_age_months": 6,
"require_plz": true
}
}
}

Rejection Handling

Common Rejection Reasons

ReasonDescriptionUser Guidance
document_too_oldDocument exceeds age limitUpload a more recent document
address_mismatchAddress doesn't match applicationUpdate address or upload correct document
name_mismatchName doesn't match applicantEnsure document is in your name
unreadableCannot extract dataUpload clearer image
unsupported_typeDocument type not acceptedUpload utility bill or bank statement

Handling Mismatches

def handle_address_verification_result(applicant, document):
verification = document.get("address_verification", {})

if verification["status"] == "matched":
# Address verified successfully
update_applicant_status(applicant["id"], "address_verified")

elif verification["status"] == "partial_match":
# Review differences
differences = verification.get("differences", [])

# Minor differences can be auto-approved
if all(d["similarity"] > 0.8 for d in differences):
update_applicant_status(applicant["id"], "address_verified")
else:
# Flag for review
create_case(
applicant_id=applicant["id"],
type="verification",
title="Address partial match requires review",
details=differences,
)

elif verification["status"] == "mismatch":
# Request new document or updated address
notify_applicant(
applicant["id"],
"Your proof of address doesn't match the address on your application. "
"Please upload a document showing your current address, or update your address."
)

Best Practices

Document Requirements Communication

Provide clear guidance to users:

Proof of Address Requirements:
- Document must be dated within the last 3 months
- Must show your full name and address
- Accepted: utility bills, bank statements, government letters
- NOT accepted: delivery receipts, screenshots, handwritten documents
- Must be a PDF or clear photo (minimum 1000x1000 pixels)

Multi-Document Verification

For high-risk applicants, require multiple documents:

def verify_address_enhanced(applicant_id):
"""Require two proof of address documents for high-risk applicants."""
documents = get_applicant_documents(applicant_id, type="proof_of_address")

verified_docs = [d for d in documents if d["status"] == "verified"]

if len(verified_docs) >= 2:
# Check addresses match each other
addresses = [d["extracted_data"]["address"] for d in verified_docs]
if addresses_match(addresses[0], addresses[1]):
return True, "Address verified with multiple documents"

return False, "Additional proof of address required"

Next Steps