April 1, 2026
Automating utility bill address verification for crypto exchanges: a step-by-step integration guide
Crypto exchanges face a tough balancing act. Regulators require proof of address verification as part of KYC onboarding, but users expect account creation to take minutes, not days. Manual document review creates bottlenecks, increases operational costs, and frustrates users who just want to start trading.
This guide walks through how to automate utility bill address verification end to end, from regulatory context to working API integration, so your compliance team can focus on edge cases instead of reading electricity bills.
Why crypto exchanges need proof of address
Most jurisdictions now require crypto exchanges to perform customer due diligence (CDD) that includes verifying a user's residential address. This isn't optional:
- EU (MiCA / AMLD6): virtual asset service providers must verify customer identity and address before establishing a business relationship
- UK (FCA): registered crypto asset firms must apply CDD measures including address verification
- US (FinCEN): money services businesses must collect and verify customer address information
- FATF Travel Rule: requires originator address information for transactions above thresholds
Failing to verify addresses can result in regulatory fines, license revocation, or losing banking relationships. But doing it manually doesn't scale.
The problem with manual utility bill review
Here's what manual verification typically looks like at a crypto exchange:
- User uploads a utility bill during onboarding
- A compliance analyst opens the document
- They manually read the name, address, and date
- They compare it against the name and address the user entered during registration
- They make a judgment call: does "J. Smith" match "John Smith"? Does "Apt 4B" match "Apartment 4B"?
- They record the decision and move to the next document
This process has several problems:
- Speed: manual review takes 2–5 minutes per document, creating backlogs during sign-up surges
- Consistency: different analysts make different judgment calls on fuzzy matches
- Language barriers: a compliance team in London can't easily read a Japanese gas bill or a Bulgarian electricity bill in Cyrillic
- Cost: at scale, manual review requires a dedicated compliance team working around the clock
- Audit trail: subjective decisions are hard to defend during regulatory audits
What to automate
An automated utility bill verification system needs to handle four checks:
- Document type: is this actually a utility bill (or bank statement, government letter, etc.) and not a screenshot of a website or a random PDF?
- Name match: does the name on the document match the user's registered name?
- Address match: does the address on the document match the user's declared address?
- Date check: was the document issued within the accepted window (typically 3–6 months)?
Each check should produce a score, not just a binary yes/no, so you can set thresholds that match your risk appetite.
Step 1: Set up your API key
Sign up at app.trusqo.com and generate an API key from your dashboard. You'll use this key in the X-API-Key header for all requests.
Step 2: Submit a verification request
When a user uploads a utility bill during onboarding, send it to the verification endpoint along with the expected name and address:
curl -X POST https://app.trusqo.com/api/verify \
-H "X-API-Key: YOUR_API_KEY" \
-F "name=Satoshi Nakamoto" \
-F "address=2-1 Nagatacho, Chiyoda-ku, Tokyo 100-8968" \
-F "files=@electricity-bill.pdf"
The API accepts PDF, JPG, and PNG files. You can also submit multiple files in a single request if your user uploads several pages.
The response includes a request ID:
{
"id": "req_abc123",
"status": "processing"
}
Step 3: Get the result
Poll for the result or configure a webhook to receive it automatically:
curl https://app.trusqo.com/api/verify/req_abc123 \
-H "X-API-Key: YOUR_API_KEY"
The response contains match scores and verdicts for every check:
{
"status": "approved",
"extractedName": "Satoshi Nakamoto",
"nameMatchScore": 0.98,
"nameMatchResult": "pass",
"extractedAddress": "2-1 Nagatacho, Chiyoda-ku, Tokyo 100-8968",
"addressMatchScore": 0.95,
"addressMatchResult": "pass",
"extractedDocType": "utility-bill",
"docTypeResult": "pass",
"extractedDateOfIssue": "2026-02-10",
"dateResult": "pass"
}
Step 4: Handle the verdict in your onboarding flow
Map the API response to your onboarding states:
- Approved: all checks pass. Activate the account automatically.
- Declined: one or more checks fail clearly (wrong name, expired document, not a valid document type). Ask the user to re-upload.
- Needs review: scores are close to the threshold. Route to a compliance analyst for manual review.
A typical integration looks like this in your backend:
// After receiving the verification result
if (result.status === "approved") {
await activateAccount(userId);
} else if (result.status === "declined") {
await requestReupload(userId, result);
} else {
await flagForManualReview(userId, result);
}
Step 5: Configure thresholds for your risk appetite
Every exchange has a different tolerance for fuzzy matches. A name score of 0.85 might be acceptable for a retail exchange but too low for an institutional platform. You can configure thresholds for:
- Name match threshold: how closely the extracted name must match (default: 0.85)
- Address match threshold: how closely the extracted address must match (default: 0.80)
- Document age: maximum age of the document in months (typically 3 or 6)
- Accepted document types: restrict to specific types like utility bills and bank statements
Step 6: Set up webhooks for real-time updates
Instead of polling, configure a webhook endpoint to receive results as soon as they're ready:
curl -X POST https://app.trusqo.com/api/webhooks \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-exchange.com/webhooks/poa-verified"}'
Your webhook endpoint receives the full verification result, so you can update the user's onboarding status in real time without polling delays.
Handling multi-language documents
Crypto exchanges serve users globally, which means utility bills in dozens of languages and scripts. A user in Bulgaria submits an electricity bill in Cyrillic. A user in Japan submits a gas bill in Japanese. A user in the UAE submits a DEWA bill in Arabic.
trusqo handles this automatically. Non-Latin text is transliterated so it can be matched against the Latin-script name and address your user entered during registration. You don't need to build language-specific logic or maintain translation tables.
Audit trail and compliance reporting
Every verification produces a downloadable PDF audit report that includes:
- The original document
- All extracted data (names, addresses, dates)
- Match scores and thresholds used
- Pass/fail reasoning for each check
This gives your compliance team a defensible record for regulatory audits. No more "the analyst thought it looked close enough."
What this looks like in production
Here's the end-to-end flow once integrated:
- User signs up and enters their name and address
- User uploads a utility bill (or bank statement, government letter, etc.)
- Your backend sends the document and expected data to the trusqo API
- Within seconds, a webhook delivers the result
- If approved, the account activates automatically
- If declined, the user is prompted to upload a different document
- If borderline, a compliance analyst reviews the flagged case with full context
Most verifications complete in under 10 seconds. For exchanges processing thousands of sign-ups daily, this eliminates the manual bottleneck entirely.
Getting started
Sign up at app.trusqo.com and get an API key. Plans start at €25/month with 50 checks included. Full API documentation is at trusqo.com/docs.