Overview
Proof-of-address (POA) verification emits warnings on poa_verifications[].warnings[] for every quality, authenticity, or matching issue detected during extraction and forensic analysis. Each warning is a Warning object with feature set to "PROOF_OF_ADDRESS". A handful of risks always force Declined; the rest follow a per-risk action (DECLINE / REVIEW / NO_ACTION) configured on your workflow.
Every risk below is verified against the live decision pipeline; the exact short and long description strings are reproduced verbatim in the tables.
POA extraction introduces a 5–15 second latency per document. Warnings are written only when the workflow step completes; do not poll faster than every 5 s.
Two production paths
POA warnings are produced on two different paths, and the risk set differs between them:
- Workflow sessions (the POA step inside a verification session) — the upload endpoint first runs blocking validation with a retry loop (see Retry behavior), then the final stored warnings are computed by
check_poa_risks plus the multi-document name check.
- Standalone API (
POST /v3/poa/) — warnings come from check_poa_risks only. There is no retry loop, no verified-ID context, and only one document, so NAME_MISMATCH_ID_VERIFICATION, POA_NAME_MISMATCH_BETWEEN_DOCUMENTS, POA_MAX_ATTEMPTS_EXCEEDED, and FUTURE_ISSUE_DATE are never produced by the standalone API. The pre-extraction validation errors are discarded on this path.
POOR_DOCUMENT_QUALITY is defined in the risk enum and reserved in the action mapping, but no code path in the POA pipeline produces it — it never appears in warnings[] on either path. Do not branch on it.
Auto-decline warnings (always force Declined)
These risks are in AUTO_DECLINE_RISKS; when logged, the POA status is Declined regardless of configuration, and log_type is always error.
| Risk | Cause | Recommended remediation |
|---|
MISSING_ADDRESS_INFORMATION | No address could be extracted from the document. | Ask the user to resubmit a document that clearly shows their address. |
POA_DOCUMENT_EXPIRED | The document is older than the configured maximum age for its type. Defaults: 3 months for UTILITY_BILL and BANK_STATEMENT, 12 months for GOVERNMENT_ISSUED_DOCUMENT and OTHER_POA_DOCUMENT (a month counts as 30 days; -1 disables the check). additional_data carries max_age_months, document_type, document_subtype, and issue_date. | Ask the user for a more recent document, or raise age_months for that type. |
INVALID_DOCUMENT_TYPE | The document could not be classified into any supported POA type (document_type falls back to UNKNOWN). | Ask the user to resubmit; check the file is not corrupted. |
UNABLE_TO_VALIDATE_DOCUMENT_AGE | The document-age check itself failed (error while computing the expiration from the issue date). | Ask the user for a document with a clearly readable issue date. |
FUTURE_ISSUE_DATE is also listed in AUTO_DECLINE_RISKS, but it has a single producer in pre-extraction validation (an issue date more than 7 days in the future) whose errors only feed the workflow retry loop — it surfaces as a blocking upload error there, is never produced by the standalone API, and is not written to warnings[]. See Retry behavior.
Configurable warnings
Each of these follows an action knob on the workflow’s POA node (DECLINE / REVIEW / NO_ACTION). The warning’s log_type mirrors the configured action: error for Decline, warning for Review, information for No action.
| Risk | Cause | Action knob | Default |
|---|
NAME_MISMATCH_WITH_PROVIDED | The name on the document scores below poa_name_match_score_threshold (default 86) against the name supplied at session creation (expected_details). | poa_name_or_address_mismatch_action | Review |
NAME_MISMATCH_ID_VERIFICATION | The name on the document scores below the threshold against the verified ID document. additional_data: { poa_name, kyc_name, match_score }. Workflow sessions only. | poa_name_or_address_mismatch_action | Review |
ADDRESS_MISMATCH_WITH_PROVIDED | The extracted address differs from the address supplied via API, or either address could not be verified as a residential address. | poa_name_or_address_mismatch_action | Review |
POA_COUNTRY_MISMATCH_WITH_PROVIDED | The document’s country does not match the country supplied via API (ISO-3 normalised). additional_data: { expected_country, extracted_country }. | poa_name_or_address_mismatch_action | Review |
POA_NAME_MISMATCH_BETWEEN_DOCUMENTS | Two or more POA documents in the same session carry different names. additional_data includes both node IDs, both names, and the match score. Workflow sessions only. | poa_name_or_address_mismatch_action | Review |
DOCUMENT_METADATA_MISMATCH | The file is empty (file_size 0) or its EXIF dates are malformed. | poa_document_issues_action | Review |
SUSPECTED_DOCUMENT_MANIPULATION | Forensics found manipulation evidence — modification after digital signing (is_tampered), overlay-text manipulation (with additional_data.manipulated_regions rectangles), a known PDF editor, inconsistent EXIF dates, or a suspicious re-export. additional_data.detection_method names the check that fired. | poa_document_authenticity_action | Decline |
UNSUPPORTED_DOCUMENT_LANGUAGE | The document’s language is not in poa_languages_allowed. | poa_unsupported_language_action | Decline |
POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION | The detected document type is not enabled in poa_documents_allowed for the document’s country, or its subtype is explicitly disabled. | poa_unsupported_document_type_action | Decline |
ISSUER_NOT_IDENTIFIED | The issuing institution could not be identified from the document. | poa_issuer_not_identified_action | Review |
UNABLE_TO_EXTRACT_ISSUE_DATE | No valid issue date could be located on the document. | poa_issue_date_not_detected_action | Review |
POA_NAME_NOT_DETECTED | No name was detected on the document. | poa_issue_date_not_detected_action (shared knob) | Review |
UNPARSABLE_OR_INVALID_ADDRESS | An address was extracted but could not be parsed and geocoded into a valid residential address. | poa_unparsable_or_invalid_address_action | Review |
POA_MAX_ATTEMPTS_EXCEEDED | The user exhausted poa_max_retry_attempts (default 2) in the workflow retry loop. additional_data: { attempts }. Workflow sessions only. | poa_max_attempts_exceeded_action | Decline |
There is one shared action for all name and address mismatches: poa_name_or_address_mismatch_action covers both name risks, both address risks, and the cross-document name check — you cannot configure name and address mismatch behavior separately. On the standalone API the same group follows the poa_address_mismatch_action request option (the poa_name_mismatch_action option is accepted but never read).
Standalone API differences (POST /v3/poa/)
The standalone endpoint builds the same risk-to-action mapping but with request-level options that only accept DECLINE or NO_ACTION (all defaulting to DECLINE):
UNABLE_TO_EXTRACT_ISSUE_DATE and POA_NAME_NOT_DETECTED are hard-coded to Decline on this endpoint.
POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION and UNPARSABLE_OR_INVALID_ADDRESS are informational here — their actions default to No action and are not configurable per request.
FUTURE_ISSUE_DATE, POOR_DOCUMENT_QUALITY, NAME_MISMATCH_ID_VERIFICATION, POA_NAME_MISMATCH_BETWEEN_DOCUMENTS, and POA_MAX_ATTEMPTS_EXCEEDED are never produced — a future-dated document is not auto-declined on this endpoint.
See the POST /v3/poa/ API reference for the request options.
Retry behavior (workflow sessions)
In a verification session, the POA upload step runs blocking validation before the document is accepted. When it finds a blocking error — INVALID_DOCUMENT_TYPE, UNABLE_TO_EXTRACT_ISSUE_DATE, FUTURE_ISSUE_DATE, POA_NAME_NOT_DETECTED, MISSING_ADDRESS_INFORMATION, POA_COUNTRY_MISMATCH_WITH_PROVIDED, POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION, UNSUPPORTED_DOCUMENT_LANGUAGE, or POA_DOCUMENT_EXPIRED — the upload is rejected with 400 and the risk code as the message, and the user can try again with a better document.
Once the user has burned poa_max_retry_attempts attempts (default 2, configurable 2–5), the submission is accepted anyway: the stored warnings are recomputed from the saved document, POA_MAX_ATTEMPTS_EXCEEDED is appended, and the status is resolved from the full risk set. This is why FUTURE_ISSUE_DATE can block an upload yet never appears in warnings[].
Configurable settings
The workflow’s POA node exposes these knobs (matching VerificationSettings fields):
| Setting | Controls | Default |
|---|
poa_name_or_address_mismatch_action | NAME_MISMATCH_WITH_PROVIDED, NAME_MISMATCH_ID_VERIFICATION, ADDRESS_MISMATCH_WITH_PROVIDED, POA_COUNTRY_MISMATCH_WITH_PROVIDED, POA_NAME_MISMATCH_BETWEEN_DOCUMENTS | Review |
poa_document_issues_action | DOCUMENT_METADATA_MISMATCH | Review |
poa_document_authenticity_action | SUSPECTED_DOCUMENT_MANIPULATION | Decline |
poa_unsupported_language_action | UNSUPPORTED_DOCUMENT_LANGUAGE | Decline |
poa_unsupported_document_type_action | POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION | Decline |
poa_issuer_not_identified_action | ISSUER_NOT_IDENTIFIED | Review |
poa_issue_date_not_detected_action | UNABLE_TO_EXTRACT_ISSUE_DATE, POA_NAME_NOT_DETECTED | Review |
poa_unparsable_or_invalid_address_action | UNPARSABLE_OR_INVALID_ADDRESS | Review |
poa_max_attempts_exceeded_action | POA_MAX_ATTEMPTS_EXCEEDED | Decline |
poa_documents_allowed | Per-country accepted document types, each with enabled and age_months (-1 = unlimited); per-subtype overrides via document_subtypes_config. | Utility bill and bank statement: 3 months; government-issued and other: 12 months |
poa_languages_allowed | Accepted document languages. | All supported languages |
poa_name_match_score_threshold | Minimum name-match score (0–100) before the NAME_MISMATCH* risks fire. | 86 |
poa_max_retry_attempts | Upload attempts before the retry loop gives up (2–5). | 2 |
Name matching logic
POA name matching tolerates common variations:
- Middle names and initials are matched leniently (presence/absence is allowed).
- Candidate names — every name detected on the document (
name_on_document plus additional_names) is scored, and the best-scoring candidate is kept.
- Match threshold — the default
poa_name_match_score_threshold is 86; a score below it fires the relevant NAME_MISMATCH* warning.
The numeric scores are exposed on the report as name_match_score_expected_details and name_match_score_id_verification (0–100). Score values are useful for fine-grained review even when no warning has been raised.
Examples
Overlay manipulation (Decline by default)
{
"warnings": [
{
"feature": "PROOF_OF_ADDRESS",
"risk": "SUSPECTED_DOCUMENT_MANIPULATION",
"additional_data": {
"reason": "Overlay-text manipulation detected: same base font embedded with multiple subset prefixes on 1 page(s).",
"detection_method": "overlay_text_manipulation",
"signals": ["duplicate_font_subset", "glyph_fragmentation"],
"duplicate_font_subsets": [{ "page": 1, "base_font": "Helvetica" }],
"fragmented_fonts": [],
"manipulated_regions": [
{ "page": 1, "x": 102.4, "y": 318.1, "width": 187.0, "height": 18.0, "page_width": 595, "page_height": 842 }
]
},
"log_type": "error",
"short_description": "Suspected document manipulation",
"long_description": "The system detected signs of potential document manipulation or editing.",
"node_id": "feature_poa_1"
}
]
}
Expired document + missing address (auto-decline)
{
"warnings": [
{
"feature": "PROOF_OF_ADDRESS",
"risk": "POA_DOCUMENT_EXPIRED",
"additional_data": {
"max_age_months": 3,
"document_subtype": "ELECTRICITY_BILL",
"document_type": "UTILITY_BILL",
"issue_date": "2025-11-02"
},
"log_type": "error",
"short_description": "Document expired",
"long_description": "The submitted document is older than 90 days from its issue date, which exceeds the acceptable time period for validity.",
"node_id": "feature_poa_1"
},
{
"feature": "PROOF_OF_ADDRESS",
"risk": "MISSING_ADDRESS_INFORMATION",
"additional_data": null,
"log_type": "error",
"short_description": "Missing address information",
"long_description": "The document does not contain complete or clear address information that can be extracted.",
"node_id": "feature_poa_1"
}
]
}
Name mismatch (Review by default)
{
"warnings": [
{
"feature": "PROOF_OF_ADDRESS",
"risk": "NAME_MISMATCH_ID_VERIFICATION",
"additional_data": {
"poa_name": "JOHN B SMYTH",
"kyc_name": "John A. Smith",
"match_score": 60
},
"log_type": "warning",
"short_description": "Name mismatch with ID verification",
"long_description": "The full name on the document does not match the name from the user's verified identity documents.",
"node_id": "feature_poa_1"
}
]
}
Warning types