Verify a payee
Use POST /v1/verify/payee before initiating a payment. v1.1 enriches payee checks with
public, read-only sources — sanctions, entity identifiers, offshore-leaks context, and
adverse media — on top of format detection and the reputation graph.
curl https://api.farofinance.app/v1/verify/payee \
-H "Authorization: Bearer $FARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Apple Inc."}'
What Faro calls (v1.1)
Checks run in parallel. One failing integration is logged and skipped; the rest still contribute signals.
| Source | What it adds | Typical signals |
|---|---|---|
| Format detection | Recognizes the payment rail (UPI, Venmo, IBAN, …) — transparency only | payee_format (info) |
| OpenSanctions | Global sanctions, PEP, and watchlist name matching | sanctions_match, sanctions_possible_match, sanctions_clean |
| GLEIF | Legal Entity Identifier lookup for business / entity names | lei_verified, lei_lapsed |
| ICIJ OffshoreLeaks (via OpenSanctions) | Offshore and shell-network context for entity names — queries both LegalEntity and Company schemas; not evidence of wrongdoing on its own | offshore_leaks_match |
| GDELT | Global adverse-media mentions (fraud, scam, default); may include a short LLM summary | adverse_media |
| Reputation graph | Prior verdicts and reports on this payee node | reputation_reports, reputation_prior_critical |
| LLM (tier 3 only) | Mule-account and scam patterns when deterministic tiers are inconclusive | llm_assessment |
OffshoreLeaks and GDELT are skipped for payment handles (UPI IDs, Venmo, etc.) — they
apply to entity / business names, not @handles or VPAs.
Global rails
Faro recognizes payee handles across payment networks worldwide and names the rail in the
payee_format signal:
| Rail | Example |
|---|---|
| UPI VPA (India) | ramesh@okaxis |
| Venmo (US) | @jane-doe-99 |
| Cash App (US) | $JaneDoe |
| Pix random key (Brazil) | 123e4567-e89b-12d3-a456-426614174000 |
| IBAN (Europe / global) | GB82WEST12345698765432 |
| Email / phone rails (Zelle, PayPal, PayID, PromptPay, PayNow…) | jane@example.com, +5511998765432 |
Example — enriched business name
A well-known company with a verified LEI may return 🟢 with signals from GLEIF and clean sanctions screening:
{
"verdict": "green",
"confidence": 0.85,
"reason": "Legal entity identifier verified for this business name.",
"recommended_action": "allow",
"signals": [
{ "name": "lei_verified", "severity": "low", "source": "gleif", "detail": "..." },
{ "name": "sanctions_clean", "severity": "info", "source": "opensanctions", "detail": "..." }
]
}
OffshoreLeaks troubleshooting
OffshoreLeaks uses your same OPENSANCTIONS_API_KEY as sanctions — there is no
separate dashboard connection. Faro scopes the OpenSanctions match API with
include_dataset=ext_icij_offshoreleaks and queries both LegalEntity and
Company in one call, then takes the best-scoring hit.
Setup checklist
- Create an API key at OpenSanctions.
- Set
OPENSANCTIONS_API_KEYin.envlocally and on Render (faro-api,faro-mcp). - Redeploy after changing env vars.
- Test with a business / legal name — not a UPI ID, Venmo handle, or IBAN.
Optional tuning:
# OPENSANCTIONS_OFFSHORELEAKS_DATASET=ext_icij_offshoreleaks # default
# OFFSHORE_LEAKS_MATCH_THRESHOLD=0.75 # default
Verify locally (faro_AI repo)
uv run python scripts/verify_offshore_leaks.py "Your Test Company Name"
Prints HTTP status, candidates per schema, and whether Faro would emit
offshore_leaks_match.
Direct OpenSanctions probe
curl -X POST "https://api.opensanctions.org/match/default?include_dataset=ext_icij_offshoreleaks" \
-H "Authorization: ApiKey $OPENSANCTIONS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"queries": {
"legal_entity": { "schema": "LegalEntity", "properties": { "name": ["TEST NAME"] } },
"company": { "schema": "Company", "properties": { "name": ["TEST NAME"] } }
}
}'
| HTTP | Meaning |
|---|---|
| 200 + empty results | API works; no match in the scoped dataset (common) |
| 401 | Invalid or missing API key |
| 403 | Plan may not include API access — see dataset terms |
Why you often see no offshore_leaks_match signal
| Reason | What to do |
|---|---|
Payee is a payment handle (VPA, @venmo, IBAN, …) | Expected — use a legal entity / company name instead |
OPENSANCTIONS_API_KEY unset | Set key; check API logs for skipping OffshoreLeaks check |
| Name not in searchable subset | OpenSanctions exposes ~1k OffshoreLeaks entities linked to their main graph, not the full ICIJ dump. Search opensanctions.org filtered to ICIJ Offshore Leaks and test an exact caption |
| Score below threshold | Lower OFFSHORE_LEAKS_MATCH_THRESHOLD (default 0.75) if you accept noisier matches |
| OpenSanctions outage / rate limit | Check fails open — other payee signals still return; retry later |
A missing OffshoreLeaks signal is neutral, not proof the integration is broken. Look
for offshore_leaks_match with severity: medium and a detail that includes
"not evidence of wrongdoing" when a match fires.
Know a bad payee Faro missed, or a false positive? Use POST /v1/reports.
See the API reference for the full schema.