Skip to content

Address Verification Guide

Last updated: 2026-05-13

This guide covers address verification providers, configuration, and how the verification system works.

Overview

Address verification standardizes and validates addresses against postal service databases. The package supports multiple verification providers with automatic selection based on country.

Verification Providers

USPS (United States Postal Service)

Best for: US addresses only Cost: FREE (rate limited to 60 requests/hour by default) Accuracy: Excellent for US addresses

Setup

  1. Register at USPS Developer Portal
  2. Get Consumer Key and Secret
  3. Add to .env:
env
USPS_CONSUMER_KEY=your_consumer_key
USPS_CONSUMER_SECRET=your_consumer_secret
ADDRESS_VERIFICATION_PROVIDER=usps

What USPS Does

  • ✅ Standardizes street abbreviations (Ave → AVE, Street → ST)
  • ✅ Adds ZIP+4 codes
  • ✅ Validates deliverability (DPV confirmation)
  • ✅ Removes special characters (O'Brien → OBRIEN)
  • ✅ Uppercases addresses for postal standards (normalized to Title Case before display and storage)

Rate Limiting

Free tier: 60 requests/hour. The package includes rate limiting:

env
USPS_RATE_LIMIT_ENABLED=true
USPS_RATE_LIMIT=60
USPS_LIMIT_BEHAVIOR=queue  # queue, fail, or fallback

Behaviors:

  • queue: Queues requests when limit reached (recommended)
  • fail: Returns error when limit reached
  • fallback: Falls back to Google verification

⚠️ Queue worker required for queue behavior: If USPS_LIMIT_BEHAVIOR=queue, a Laravel queue worker must be running or queued requests will never process. Run php artisan queue:work locally, or configure a process supervisor in production. See FAQ — Bulk operations run but nothing happens.

Smarty International Street API

Best for: Global coverage including countries Google does not support Cost: ~$0.006 per request Coverage: 240+ countries (US via US Street API, all others via International Street API)

Setup

  1. Register at smarty.com
  2. Get Auth ID and Auth Token from your account
  3. Add to .env:
env
SMARTY_AUTH_ID=your_auth_id
SMARTY_AUTH_TOKEN=your_auth_token
ADDRESS_VERIFICATION_PROVIDER=auto

Two licenses are required for full coverage:

  • us-core-cloud — US Street API (default for US addresses)
  • international-cloud — International Street API (default; check your Smarty account for the exact value)

What Smarty Does

  • ✅ Full US DPV confirmation (same data as USPS)
  • ✅ 240+ country international coverage
  • verification_status: Verified / Partial / Ambiguous / None
  • ✅ Geocoordinates included in response
  • ✅ Rooftop-level precision for supported countries
  • Partial results (street-level only) set needs_review = true

Google Address Validation API

Best for: International addresses in ~40 supported countries Cost: $0.005 per request (~$5 per 1,000 addresses) Coverage: ~40 countries (AR, AU, AT, BE, BR, CA, CL, CO, CZ, DK, EE, FI, FR, DE, HU, IE, IL, IT, JP, KR, LV, LT, MX, NL, NZ, NO, PL, PT, PR, RO, SG, SK, SI, ZA, ES, SE, CH, TW, GB, US)

Note: China (CN), India (IN), and South Korea (KR) are not reliably supported in practice. CN and IN return "No Provider" cleanly. KR is listed in Google's coverage docs but the API may return an error — the package handles this gracefully and shows "Address verification is not available for this country." Use auto provider to route these countries to Smarty automatically.

Setup

  1. Enable "Address Validation API" in Google Cloud Console
  2. Add to .env:
env
GOOGLE_MAPS_SERVER_KEY=your_server_key  # Address Validation API + Geocoding API
ADDRESS_VERIFICATION_PROVIDER=google

What Google Does

  • ✅ International address formatting
  • ✅ Address component extraction
  • ✅ Coordinate geocoding included
  • ✅ Supports local language formats
  • ✅ Less aggressive standardization than USPS

Automatically chooses the best available provider based on country:

env
ADDRESS_VERIFICATION_PROVIDER=auto

Selection Order

For each address, the system works through three layers in order:

1. Country pins (country_providers config) — hard-wired overrides for specific countries:

php
// config/addresses.php
'country_providers' => [
    'US' => 'usps',   // Always use USPS for US
],

2. Smart routing — when both Smarty and Google are configured and the country is not pinned, the system selects the better provider based on Smarty's published coverage tiers:

  • Countries where Smarty has DeliveryPoint precision (CA, AU, GB, FR, DE, BR, and 150+ others) → Smarty tried first
  • All other countries (JP, and others where Smarty precision is lower or empirically unreliable) → Google tried first

Within each group, the losing provider is still tried as a technical fallback. If Google has a billing lapse, Smarty takes over — and vice versa. Technical failures (auth error, billing inactive, rate limit) cause automatic failover to the next provider. Address-level failures (address not found, DPV=N) stop the chain — a different provider won't fix a bad address.

3. No provider available → verification skipped (Verify button hidden in UI).

Default Behavior (all three providers configured)

CountryProviderReason
USUSPSPinned in country_providers
CA, AU, GB, FR, DE, BR, IL, SG…SmartySmart routing: Smarty has DeliveryPoint precision
JPGoogleSmart routing: Smarty listed as DeliveryPoint but unreliable in practice
CN, IN, RU…SmartyGoogle doesn't cover these; Smarty does
Any uncoveredNo provider available

Common Customizations

php
// US clients only — no international needed
'provider_priority' => ['usps'],

// International-first, no USPS
'provider_priority' => ['smarty', 'google'],

// Premium provider for key markets, cheaper elsewhere
'country_providers' => [
    'US' => 'usps',     // Free and authoritative
    'GB' => 'smarty',   // Rooftop precision for UK office
    'DE' => 'smarty',   // German market priority
],
'provider_priority' => ['google', 'smarty'], // Google as default fallback

Provider Comparison

USPS vs Smarty for US Addresses

Both providers produce functionally identical results for US addresses. Either is a sound choice.

BehaviorUSPSSmarty
Address normalizationIdenticalIdentical
ZIP+4Always addedAdded when available
DPV needs_review✅ S/D codes flagged✅ S/D codes flagged
State format returnedFull name ("California")Abbreviation ("CA") — fuzzy-matched to full name
CostFree (rate limited 60/hr)~$0.006/request

Recommendation: Use USPS for US-only deployments — it's free and equally accurate. Use Smarty if you need international coverage, to avoid managing two separate providers.

Google vs Smarty for International Addresses

BehaviorGoogleSmarty
Coverage~40 countries240+ countries
China (CN), India (IN)❌ Not supported — clean "No Provider" path✅ Supported
South Korea (KR)❌ Removed from supported list — API errors in practice✅ Supported
Landmark addresses (no street number)❌ Fails consistently❌ Also fails (same root cause)
Intersection formats (e.g. "St A & St B")✅ Resolves to primary street❌ Rejects format
Street name normalizationMay abbreviate or expand per local conventionExpands abbreviations; may correct to official name
Administrative area extrapolation✅ Inferred from city/postal (FR, AU, DE city-states)Only from API response
Postal code correctionsCorrects frequently; may over-correctCorrects against postal database
State name format (MX)May abbreviate ("Jal.")Returns full name ("Jalisco")
Administrative area level (FR)Région ("Île-de-France") — stored in administrative_areaDépartement ("Paris") — stored in administrative_area at level 2
JP block number in chōme addressesRetained ("1 2-chōme Shibuya")Dropped ("1 Chome Shibuya")
address_line_2 contentUnit/landmark fragment onlyDelivery-line packing (CA, FR, DE) stripped: values containing the postal code or locality are discarded; MX colonia and JP/CN building values preserved
Diacritics / accented characters✅ Preserved in UTF-8 ("Rue de la République")⚠️ Follows postal authority standard — many countries (FR, ES, IT, PT…) use uppercase ASCII without diacritics ("RUE DE LA REPUBLIQUE"). See Special Character Handling.

Recommendation: Use auto provider (the default). It routes US to USPS (free), international to Smarty (240+ countries). Google alone has gaps at CN/IN/KR and fails landmark addresses; Smarty alone costs money for US addresses that USPS handles for free.

Country-Specific Behavior Notes

CountryNotes
USUSPS and Smarty are equivalent. USPS is free.
GBBoth work for street addresses ✅. Postcodes may be corrected — review via standardization dialog. Smarty has been observed matching to the wrong city for addresses where the street name exists in multiple locations (e.g. "Downing Street" → wrong postcode).
CABoth work ✅. Postal code format preserved (A1A 1A1). Both providers independently corroborate postal corrections.
MXBoth work ✅. Smarty returns full state name; Google may abbreviate ("Jal." for Jalisco). Smarty extracts the colonia (neighborhood) into address_line_2 — useful secondary data.
FRBoth work ✅. France uses administrative_area_level=2 — the département is stored in administrative_area (101 departments seeded). Google previously returned the région; it now matches the département via fuzzy match. Smarty has always returned the département — no change needed. Entering a postal code auto-populates the département (also works for DE, ES, IT, and NL). The région (level 1) is not stored on the record but is derivable via the département's parent_id for reporting. City is free-text locality. Google may substitute the official postal address for landmarks (e.g. Eiffel Tower → Av. Gustave Eiffel). Accented characters: Smarty returns French addresses per La Poste standard — uppercase ASCII without diacritics (e.g. RUE DE LA REPUBLIQUE, not Rue de la République). Google preserves accents. If diacritic preservation matters, use Google or choose "Keep My Version" in the standardization dialog.
DEBoth work ✅. Blur geocoding requires the state field (Bundesland) to be filled first for non-city-states. Berlin works without it (city-state). Entering a postal code auto-populates the Bundesland.
AUStreet addresses work ✅. Landmarks and intersections: Google resolves intersections to a street; Smarty rejects them. Landmark names without street numbers fail both.
JPBoth work for standard addresses ✅. Romanization is preserved — chōme format offered in standardization dialog, not written directly. Google retains the block number; Smarty may drop it.
CNGoogle Address Validation not supported. Without Smarty: ROOFTOP/RANGE_INTERPOLATED geocoding results are promoted to google_geocoding verified — badge shows "✓ Verified by Google Geocoding". With Smarty ✅: Smarty covers CN but may match common street names to the wrong city (e.g. "Jianguo Road" matched to wrong province) — always review the standardization dialog for CN addresses.
INGoogle not supported — clean "No Provider". Smarty covers IN for street addresses ✅. Landmark-only addresses (no street number) fail verification — enter a street address with a house number.
KRGoogle excluded from KR support (API errors in practice). auto routes to Smarty automatically.

Landmark Addresses

Named landmarks without a street number (e.g. "Opera House", "Buckingham Palace") consistently fail both Google and Smarty. The geocoder cannot extract a street component, so the verification provider receives an incomplete address and rejects it.

Workaround: Enter the street address and house number instead of the landmark name. The standardization dialog will show the provider's canonical form — for well-known landmarks Google may return an official postal address (e.g. the Eiffel Tower's address is "Av. Gustave Eiffel, 75007 Paris").

Geocoding Confidence and Sanity Checks

Even when verification succeeds, the package applies additional quality checks:

Geocoding confidence scoring: The Google Geocoding API returns a location_type indicating precision. Results with GEOMETRIC_CENTER or APPROXIMATE location types automatically set needs_review = true — the address saved but should be confirmed by a human. ROOFTOP and RANGE_INTERPOLATED results are accepted without flagging.

Geocoding-as-verification fallback: For countries not covered by any configured postal provider (e.g. CN, most of Africa/Central Asia), a ROOFTOP or RANGE_INTERPOLATED geocoding result is promoted to verified status — is_verified = true, verification_provider = 'google_geocoding'. This reflects that the address is genuinely well-located; it is not unverified because of bad data, but because no postal provider architecturally covers the country. The badge shows "✓ Verified by Google Geocoding". Low-confidence results (GEOMETRIC_CENTER, APPROXIMATE) are not promoted.

Verification sanity checks: Before accepting a verified result, the package checks for:

  • Blank address_line_1 — if the provider returns an empty street, the original user input is preserved
  • Empty city — if the provider returns no city, the user's city is kept
  • Cross-country postal code — if the postal code belongs to a different country than selected, the result is rejected

These checks run automatically on every verification and require no configuration.


None (Disable Verification)

env
ADDRESS_VERIFICATION_ENABLED=false
# OR
ADDRESS_VERIFICATION_PROVIDER=none

Addresses are saved as entered without verification. Useful for:

  • Development/testing
  • Addresses that don't need validation
  • Reducing API costs

How Verification Works

User Flow

  1. User enters address (manually or via map/autocomplete)
  2. System verifies address on save using configured provider
  3. If differences found → Shows verification choice dialog
  4. User chooses → "Use Standardized" or "Keep My Version"
  5. Address saved with verification metadata

Verification Metadata

Stored in address record:

  • is_verified (boolean)
  • verification_provider (usps, smarty, google, google_places; null when not verified)
  • verified_at (timestamp)
  • verification_metadata (JSON with full response)

Verification Choice Dialog

When it appears:

  • Verification found differences that might change meaning
  • User needs to decide which version to use

When it does NOT appear:

  • ✅ Address verified successfully with no differences
  • ✅ Address matches verification exactly
  • ✅ This is the optimal UX - seamless verification!

You may ask: "Why don't I see the verification dialog?"

Answer: The dialog only appears when needed. If verification succeeds seamlessly, you won't see it. This is intentional and means everything worked perfectly!


Understanding the Verification Comparison

Why It Exists

The comparison appears when there are meaningful differences between what you entered and what the postal service database shows. You need to decide which version to keep.

What It Looks Like

The comparison is not a modal — it appears inline within the form, below the address fields, and scrolls into view automatically. It has an amber top accent and a warning icon in the header.

Header: "Address Standardized by {Provider}" (e.g. "Address Standardized by USPS")

Two side-by-side cards:

Your InputStandardized Version
Original badge (gray)Recommended badge (green)
Changed fields highlighted in amberChanged fields highlighted in green
Unchanged fields in normal textUnchanged fields in normal text

Only fields with non-empty values are shown. Empty fields (e.g. address_line_2 if blank) are omitted from both cards.

Changes bar: Below the cards, a summary line lists which fields differ — e.g. "Changes detected: address line 1, locality, postal code"

Buttons:

  • Keep My Version — gray outlined button; applies your original input and marks the address as verified (the provider confirmed it was deliverable, even if you prefer your formatting)
  • Use Standardized — green button with a checkmark icon; applies the provider's standardized version

When to Use Standardized

Use standardized in most cases:

  • Provider corrected a street name, city, or postal code → accept it
  • ZIP+4 was added → accept it (improves mail routing, no downside)
  • Street abbreviations expanded or normalized → accept it

When to Keep Your Version

  • Your suite/unit format is required by your organization and the provider's version differs only in formatting
  • Address is new construction not yet in the postal database
  • Provider has mapped to the wrong street (rare, but possible — especially for common street names in dense areas)

Verification Without Seeing the Dialog

How to Confirm It's Working

If you don't see the verification dialog, here's how to confirm verification is happening:

Method 1: Check the UI

  1. Save an address
  2. View in address table
  3. Look for ✅ green checkmark icon in "Verified" column
  4. Hover over checkmark → See "Verified by USPS on Feb 15, 2026"

Method 2: Check the Database

bash
php artisan tinker
php
use Viewflex\FilamentAddress\Models\Address;

// Check recent addresses
$recent = Address::orderBy('created_at', 'desc')->take(5)->get();

foreach ($recent as $addr) {
    echo $addr->address_line_1 . PHP_EOL;
    echo '  Verified: ' . ($addr->is_verified ? 'YES' : 'NO') . PHP_EOL;
    echo '  Provider: ' . ($addr->verification_provider ?: 'none') . PHP_EOL;
    echo '  At: ' . ($addr->verified_at ? $addr->verified_at->format('Y-m-d H:i') : 'never') . PHP_EOL;
}

// Count verified addresses
echo "\nTotal verified: " . Address::where('is_verified', true)->count();

Method 3: Check Laravel Logs

bash
tail -f storage/logs/laravel.log | grep -i verif

Look for:

[info] Address verified by USPS: 123 Main St
[info] Verification successful, no differences

Integration with Blur Geocoding

The package integrates verification with blur geocoding seamlessly:

What Happens

  1. User types address in address_line_1 field
  2. User tabs out (blur event)
  3. Geocoding runs → Gets full address from Google
  4. Verification runs → Validates/standardizes address
  5. Form auto-fills → All fields populated with verified data
  6. No dialog shown → Because it all happened automatically!

Why This is Better UX

Traditional verification:

  1. User fills all fields manually
  2. Clicks save
  3. Dialog appears → User must review differences
  4. User clicks "Use Standardized"
  5. Form reloads with new data

Our integrated approach:

  1. User types partial address
  2. Tabs out
  3. ✨ Everything happens automatically
  4. User sees final verified address immediately
  5. Can make adjustments before saving

Result: Faster, fewer clicks, less cognitive load.


Verification Best Practices

For New Addresses

There are three ways to enter an address. Choose based on what your users are doing:

Best when the user is looking up an address they can recognize in a dropdown — customer-facing forms, CRM entry from known contacts, anything where the user can search and select.

  • User types into the search box; Google Places suggestions appear
  • One selection fills all fields — street, city, state, postal code, coordinates
  • Marked as verified automatically (google_places); no Verify button press needed
  • API cost: one session-billed Places request per selection (not per keystroke)

If you need postal deliverability confirmation (DPV, ZIP+4) beyond what Places provides, click Verify after autocomplete to run USPS or Smarty on top of the Places-filled data. This is worth doing for US addresses where deliverability matters.

Blur geocoding (type into Address Line 1, tab out)

Best for staff entering addresses from a document, letter, or label — where the user is reading and typing, not searching. Also handles paste-and-tab workflows.

  • User types (or pastes) a street address into Address Line 1, then tabs out
  • Geocoding runs automatically and fills remaining fields (city, state, postal code, coordinates)
  • User then clicks Verify to run the configured provider and see the comparison if differences exist
  • API cost: one Geocoding API call + one verification API call

The geocoding step handles partial input well — a street address with just a city is often enough to fill the rest. Configuring ADDRESS_BLUR_REQUIRE_COMPLETENESS=basic prevents wasted API calls on incomplete input.

Manual entry

Use only when the address is not in Google's database — new construction, rural routes, or addresses that consistently fail geocoding. Every field must be filled by hand, and verification is the only quality check.

❌ Avoid regardless of mode:

  • Saving without clicking Verify (or confirming the autocomplete result is correct)
  • Always choosing "Keep My Version" without reading what changed
  • Disabling verification to save time — the cost of bad addresses in your database is higher than the API cost

For Existing Addresses

✅ Recommended:

  1. Use bulk verification action for batches
  2. Review flagged addresses needing attention
  3. Keep verified status up-to-date

❌ Avoid:

  1. Manually editing verified addresses (loses verification status)
  2. Importing unverified data over verified data

For Bulk Operations

✅ Recommended:

  1. Import with verify: false (faster initial import)
  2. Use bulk verify action after import
  3. Review addresses that need review (needs_review = true)

❌ Avoid:

  1. Verifying during import (slow, expensive)
  2. Ignoring addresses that need review

Special Character Handling

What Verification Does to Special Characters

USPS behavior:

  • Apostrophes removed: O'BrienObrien
  • Hyphens in unit designators standardized: #5-BApt 5b
  • Parentheses removed: (Rear) → removed
  • USPS returns all-uppercase internally; the package normalizes to Title Case before displaying in the dialog or storing in the database

Google behavior:

  • Less aggressive
  • Preserves more formatting
  • Title Case typical
  • Diacritics (accents, umlauts, etc.) preserved in UTF-8

Smarty International behavior:

  • Returns addresses following each country's postal authority standard
  • Many postal authorities (France La Poste, Spain Correos, Italy Poste Italiane, Portugal CTT, Brazil ECT, and others) mandate uppercase ASCII without diacritics — this is what appears on physical mail
  • Examples: Rue de la RépubliqueRUE DE LA REPUBLIQUE, Calle AlcaláCALLE ALCALA, Piazza dell'UnitàPIAZZA DELL'UNITA
  • Germany (Deutsche Post) and some Northern European postal authorities preserve case and diacritics
  • The standardization dialog shows both your input and the Smarty-standardized form side by side — if preserving accents matters for your use case, choose "Keep My Version"

Example

Input:

123 O'Brien's Ave, Apt #5-B (Rear)
Washington, DC 20500

As displayed in the standardization dialog:

address_line_1: 123 Obriens Cir
address_line_2: Apt 5b
Winchester, VA 22602

Why the differences?

  1. Street type changed (Ave → Cir): USPS database shows this is actually a Circle, not Avenue
  2. Special characters removed and unit normalized: Apostrophes and parentheses stripped; unit designator moved to address_line_2 in standardized form
  3. City changed: Address was actually in Winchester, not Washington

This is correct! USPS found the real address and corrected user mistakes.


Verification Costs

Cost Comparison

ProviderCost per RequestCoverageFree Tier
USPSFREEUS only60/hour (no paid tier)
Smarty~$0.006240+ countriesPlan-based
Google$0.005GlobalNone

Smarty and Google costs are estimates — check current pricing on their respective sites.

Cost Optimization

  1. Use auto provider → Free USPS for US addresses, paid provider only for international
  2. Cache results → Enabled by default (30-day TTL); repeated verifications of the same address are free
  3. Bulk operations → Verify after import rather than during, to avoid redundant API calls
  4. Rate limiting → USPS rate limiter prevents accidental overage

Example Costs

1,000 US addresses:

  • With USPS: $0 (within rate limit)
  • With Google: $5

1,000 international addresses:

  • With Smarty: ~$6
  • With Google: $5

10,000 mixed addresses (auto provider):

  • 5,000 US via USPS: $0
  • 5,000 international via Smarty: ~$30
  • Total: ~$30 (vs $50 if all Google)

Troubleshooting

Verification Not Working

Check configuration:

bash
php artisan tinker
php
// Check verification enabled
config('addresses.verification.enabled')  // should be true

// Check provider
config('addresses.verification.provider')  // auto, usps, google, or none

// Check API keys
config('addresses.google_maps_server_key')  // should have value (used for Google verification)
config('addresses.verification.usps.consumer_key')  // should have value

Check Laravel logs:

bash
tail -f storage/logs/laravel.log | grep -i verif

USPS Returns Errors

Common errors:

"USPS API Error: Invalid API Key"

  • Double-check Consumer Key/Secret in .env
  • Verify keys are activated in USPS account

"Rate limit exceeded"

  • Wait for hourly reset, OR
  • Change to USPS_LIMIT_BEHAVIOR=fallback to use Google when limited

"Address not found"

  • Address might not exist in USPS database
  • Try "Keep My Version" or use Google instead

Google Returns Errors

"API key not valid"

  • Check API key in .env
  • Verify Address Validation API is enabled in Google Cloud Console
  • Check API key restrictions

"You have exceeded your daily quota"

  • Check Google Cloud Console for quota limits
  • Increase quota or wait for reset

Dialog Not Appearing (Normal!)

This is usually correct behavior. See Verification Without Seeing the Dialog above.


Verification Fields Reference

These fields on the Address model track verification state:

FieldTypeDescription
is_verifiedbooleantrue when an external service has confirmed the address
needs_reviewbooleantrue when verification found meaningful differences awaiting user review
verification_providerstring|nullWhich service verified: usps, smarty, google, google_places, google_geocoding; null when not verified
verified_attimestamp|nullWhen verification occurred
verification_metadataJSON|nullRaw provider response plus a context key

Verification Status Badge

The form displays a badge below the address fields reflecting the current verification state:

BadgeMeaning
🟢 ✓ Verified by {Provider}Postal-verified (USPS, Smarty, Google, Google Geocoding)
🔵 ✓ Accepted by userManually accepted — user dismissed a review prompt
🔵 ✓ Accepted — Location VerifiedGoogle Places selection with postal verification pending
🟡 ⚠ Needs ReviewVerification found differences or a POI was selected
🟡 Not VerifiedNo verification has been run yet

Clickable badge popover: The green and blue accepted badges are clickable. Clicking opens a popover showing the provider name, verification date, and a Re-verify Address button. This gives staff a clean entry point for re-running verification on already-verified addresses without exposing a permanent Re-verify button on the form. The Re-verify button is omitted when verification is not available for the current country or when no provider is configured.

needs_review Lifecycle

Set to true in these situations:

  • Bulk verification — the standardized version differs meaningfully from the stored address. The address manager shows a warning badge; the user resolves each by choosing "Use Standardized" or "Keep My Version".
  • Google Places selection of a POI (no street address) — the selected place is a landmark or business with no civic address; the user should confirm the street before saving.
  • Low-confidence geocoding — Google returned GEOMETRIC_CENTER or APPROXIMATE location type, indicating the coordinates may not be precise.
  • Silent verification failure — in silent mode, a failed verification sets needs_review = true for staff review in the admin panel.

Set to false:

  • When the user resolves a bulk-verification conflict ("Use Standardized" or "Keep My Version").
  • When the user clicks "Accept Address" — shown for Places-verified addresses in countries where no verification provider is configured (no API call, just clears the flag).
  • When the user clicks Verify (or Re-verify) and verification succeeds.
  • On country change (resets all verification state).

Street mismatch between Places and reverse geocode is recorded in verification_metadata['street_mismatch'] for auditing but does not set needs_review. The user's Places selection is the authoritative street; the reverse geocode is used only to populate administrative subdivisions.

DPV=S and quality scoring: USPS and Smarty return DPV=S when the primary address matches but the secondary unit is missing or uses a different designator than USPS has on file ("2B" vs "APT 2B"). When needs_review is set as a result, the quality score treats the address as partially confirmed (lower score). When the user accepts the USPS-standardized form — clearing needs_review — the concern is resolved and the quality score reflects a fully confirmed address. The verification_metadata retains the original DPV code for auditing regardless.

Quality scoring for countries with no provider coverage: Unverified addresses in countries not covered by any configured postal provider (e.g. CN without Smarty, most of Africa/Central Asia) receive a neutral verification score (20 pts, same as google verified) rather than a zero penalty. The address is unverified because no provider covers the country — not because of bad data. This guard only applies when at least one provider is configured globally; environments with no API keys score 0 as normal.

verification_metadata Structure

The JSON object contains provider-specific data merged with two common keys added by the package:

  • context — how the verification was triggered (see values below)
  • verified_at_timestamp — ISO 8601 timestamp of verification

The provider-specific keys differ by provider:

USPS:

json
{
    "usps_response": { "streetAddress": "123 Main St", "city": "Springfield" },
    "additional_info": { "deliveryPoint": "12", "checkDigit": "1" },
    "dpv_confirmation": "Y",
    "needs_review": false,
    "original_input": { "address_line_1": "123 main st", "locality": "springfield" },
    "context": "manual_entry",
    "verified_at_timestamp": "2026-04-03T12:00:00+00:00"
}

When needs_review is true, a review_reason key is also present explaining the DPV code (e.g. missing secondary information).

Smarty (US Street API):

json
{
    "dpv_match_code": "Y",
    "dpv_footnotes": "AABB",
    "dpv_vacant": "N",
    "rdi": "Residential",
    "precision": "Zip9",
    "context": "manual_entry",
    "verified_at_timestamp": "2026-04-03T12:00:00+00:00"
}

Smarty (International Street API):

json
{
    "verification_status": "Verified",
    "address_precision": "Premise",
    "geocode_precision": "Rooftop",
    "needs_review": false,
    "context": "manual_entry",
    "verified_at_timestamp": "2026-04-03T12:00:00+00:00"
}

Google Places selection (not a provider verification — set when user selects from autocomplete):

json
{
    "source": "google_places_selection",
    "has_street_address": true,
    "street_mismatch": false,
    "context": "map_selection",
    "verified_at_timestamp": "2026-04-03T12:00:00+00:00"
}

Context values:

  • manual_entry — User submitted or re-verified via the form
  • map_selection — User selected a location from the Places autocomplete or map
  • bulk_verification — Triggered by a bulk verification operation
  • import — Triggered during a CSV/Excel import

Planned Enhancements

No planned enhancements at this time.


See Also

Released under a commercial license.