IDCanopy Developers
Umbrella API

Address Verification

Check that a person lives at an address, with a score, a corrected address and an OK, REVIEW or NOK decision

What it does

POST /address/verify checks a person (first name, last name, date of birth) against a postal address. It returns a match level, a score from 0 to 100, the address in its confirmed form and a decision: OK, REVIEW or NOK.

It answers "does this person live at this address", not "does this address exist". A valid address with the wrong person on it scores low.

Coverage. The service verifies addresses internationally. DE and AT are handled by the primary postal source, which also corrects addresses. All other countries are handled by the international source. Switzerland (CH) is a current limitation, see Which source answers.

Base URL: see Environments. Authenticate as described in Authentication. The full schema is in the Umbrella API reference.

Request

{
  "country": "DE",
  "address": {
    "street": "Musterstrasse",
    "number": "12",
    "zip": "10115",
    "city": "Berlin"
  },
  "identity": {
    "firstname": "Erika",
    "lastname": "Mustermann",
    "dob": "1985-04-12"
  }
}
FieldRequiredDescription
countryyesCountry code, two or three letters. Case and surrounding spaces do not matter.
address.streetyesStreet name, without the number.
address.numberyesHouse or building number, as a string. Keep suffixes and flat numbers, for example 39/16.
address.zipyesPost code, as a string.
address.cityyesTown or city.
address.provincenoAccepted and ignored.
identity.firstnameyesFirst name.
identity.lastnameyesLast name.
identity.dobyesDate of birth, YYYY-MM-DD. Do not send an empty value.

Country handling. DE, AT and CH are recognised in both forms (DEU, AUT, CHE are converted). Any other value is passed on as sent, so send a valid ISO 3166-1 code.

A key repeated anywhere in the JSON body is rejected with 400.

Known issue. The service does not check the body for missing fields. A request without a required field ends in a 500 instead of a 400. Send every required field.

Which source answers

Each request is answered by exactly one source, chosen by country. The routing is fixed and not configurable per account.

CountrySourceCorrects the addressWhat you get
DE, ATPrimary postal source (postal reference data with person records)yesaddressStatus can be corrected; addressComponents holds the confirmed address
CHnonen/aCurrent limitation: always NOK, score 0, noMatch. Switzerland is not verified at present.
every other countryInternational source (commercial identity and address data)noaddressStatus is always unchanged; finalAddress and addressComponents repeat your input

There is no fallback: if the selected source cannot be reached or fails, the call ends in a 500 and no second source is tried. Retry later; a failed call is not a NOK and says nothing about the person.

Access is per account: an account without the service gets 403, and an account with a transaction limit gets 429 once the limit is used.

Response

{
  "inputAddress": "Musterstrasse 12, 10115, Berlin, DEU",
  "correctedAddress": "Musterstraße 12, 10115, Berlin, DEU",
  "finalAddress": "Musterstraße 12, 10115, Berlin, DEU",
  "addressStatus": "corrected",
  "addressComponents": {
    "street": "Musterstraße",
    "number": "12",
    "zip": "10115",
    "city": "Berlin",
    "country": "DEU"
  },
  "matchQuality": "EXACT",
  "score": 95,
  "globalResult": { "overall": "OK", "totalScore": 95 },
  "identity": { "fullName": "Erika Mustermann", "dob": "1985-04-12" },
  "extendedMessage": "addressCorrected"
}
FieldTypeMeaning
inputAddressstringYour address as one line: street number, zip, city, country. For DE and AT the country is the three-letter code.
correctedAddressstringSame value as finalAddress.
finalAddressstringThe address to store. For DE and AT it is the confirmed address from the source, which can differ from your input. Otherwise it equals inputAddress. Treat it as display text and use addressComponents for fields.
addressStatusstringcorrected if the source changed any part of the address, else unchanged.
addressComponentsobjectstreet, number, zip, city, country. province is never returned. For DE and AT these are the confirmed values, and empty strings when no address could be confirmed. Otherwise they repeat your input. Pending verification: the country format in the confirmed address.
matchQualitystringSee the table below.
scoreinteger0 to 100.
globalResult.overallstringThe decision: OK, REVIEW or NOK.
globalResult.totalScoreintegerThe same value as score.
identity.fullNamestringfirstname lastname as you sent it.
identity.dobstringDate of birth as you sent it.
extendedMessagestringDetail for the result, see Reason codes. Often an empty string. Do not branch on it.

ERROR is listed in the schema as a value of globalResult.overall but the service never returns it. Failures are HTTP errors.

matchQuality

ValueMeaning
EXACTPerson confirmed at the address (with or without a spelling correction)
HOUSEHOLD_MATCHSomeone with the same surname lives at the address (DE, AT)
STREET_MATCHPerson confirmed on the street, house number missing or different
PARTIAL_MATCHPerson and address match, but the street is not confirmed (international source)
CITY_MATCHPerson known in the city or post code, not at the address
IDENTITY_MISMATCHAddress matches but the name or date of birth only partly does (international source)
NO_MATCHNot confirmed

HOUSENUMBER_MATCH is in the schema and appears only in the sandbox test cases.

Decision logic

The decision is a fixed mapping from the source's result to a score, a matchQuality and overall. There is no threshold you can configure, and no weighting of fields you can change.

overallScoreWhat to do
OK80 to 100Treat the address as verified. Store finalAddress.
REVIEW50Send to a person, or ask the customer for a proof of address. Do not decline on this alone.
NOK0 to 25Not verified. Decline the address, or ask for another proof. deceased and addressFakeSuspicion deserve a closer look than a plain noMatch.

Scores are not continuous. Use overall for the decision and score to rank or report.

DE and AT: score by source result

Source resultScorematchQualityoverallextendedMessage
Person confirmed at the address, nothing changed100EXACTOKempty
Person confirmed, address spelling or format corrected95EXACTOKaddressCorrected
Same surname at the address80HOUSEHOLD_MATCHOKempty
Person confirmed on the street (AT)80STREET_MATCHOKempty
Address correct, building not found (DE)50CITY_MATCHREVIEWempty
Person confirmed in the city (AT)50CITY_MATCHREVIEWcityMatch
Person known in the data, not at this address (AT)25NO_MATCHNOKincorrectAddress
Address wrong or ambiguous (DE)25NO_MATCHNOKnoMatch
Person lived here before (DE)25NO_MATCHNOKpreviousAddress
Only the address found, person not found (AT)0NO_MATCHNOKidentityNotFound
Person not found (DE)0NO_MATCHNOKNoMatch
Suspected fake address (DE)0NO_MATCHNOKaddressFakeSuspicion
Person deceased0NO_MATCHNOKdeceased
Address structurally defective, or no result0NO_MATCHNOKnoMatch
Source timed out (DE)0NO_MATCHNOKempty

A timeout reported by the source is returned as a NOK with an empty extendedMessage and is not distinguishable from a real non-match except by that empty message. Pending verification: whether the source also fails the HTTP call in that case.

Other countries: name score times address score

The international source reports which parts matched. The service turns them into two sub-scores, multiplies them and maps the result.

Address score

MatchedAddress scoreMessage
Street, number, post code and city100
Street and number, post code, no city90NoCity
Street and number, city, no post code90NoPostCode
Street and city or post code, no number80
Number, city and post code, no street70NoStreet
City and post code only50cityMatch
Post code only50postcodeMatch
District only20districtMatch
County only, or province only15, 10
Nothing but a failed locality10localityMatch
Every address part failed0

Name score

MatchedName scoreMessage
First and last name, date of birth not contradicted100
First and last name, date of birth partly matches50dobPartial
First and last name, date of birth contradicted20dobFailedFull
Last name only, date of birth confirmed50lastNameOnly
Last name only, date of birth contradicted20dobFailedFull
First name only, date of birth confirmed50firstNameOnly
First name only, date of birth partly matches40dobPartial
First name only, date of birth contradicted0dobFailedFull
Neither name matched0

Combination. address score × name score ÷ 100 is rounded to the nearest ten (a value of exactly 100, 95 or 25 is kept), anything below 25 becomes 0, and anything between 25 and 50 becomes 25. The result maps as follows.

ResultScore returnedmatchQualityoverallextendedMessage
100100EXACTOKExact Match
9090EXACTOKNoCity or NoPostCode
8080STREET_MATCHOKempty
7050PARTIAL_MATCHREVIEWNoStreet
50, from a name message50IDENTITY_MISMATCHREVIEWthe name message
50, from the address50CITY_MATCHREVIEWcityMatch or postcodeMatch
2525NO_MATCHNOKsee below
00NO_MATCHNOKthe name message, else noMatch

On a 25 the message is an address-level label and can be one of the address messages above or a plain word such as Full. Read it as informational. A full name match with a wrong date of birth scores 20 on the name, which brings the result to 0 or 25 whatever the address: a verified address does not rescue a contradicted date of birth.

Reason codes

extendedMessage explains a result. The code is stable enough to log and show to an agent. It is not a contract for automation: new codes can appear and an empty string is normal, so branch on globalResult.overall.

CodeSeen onMeaningWhat to do
emptyallNo extra detailNothing
addressCorrectedDE, AT, othersSpelling or format of the address was correctedStore finalAddress
Exact MatchothersEverything matchedNothing
NoCity, NoPostCode, NoStreetothersThat address part was not confirmedAccept per overall; show to an agent on REVIEW
cityMatch, postcodeMatch, districtMatch, localityMatchAT, othersMatch only at that levelReview
incorrectAddressAT, othersPerson known, address does not fitAsk for the current address
previousAddressDEPerson lived at this address beforeAsk for the current address
identityNotFoundATAddress found, person not foundReview the name and date of birth, ask for proof
noMatch, NoMatchallNothing usable foundDecline or ask for another proof
addressFakeSuspicionDEAddress suspected to be inventedDecline and escalate
deceasedDE, ATPerson recorded as deceasedDecline and escalate
dobPartialothersDate of birth matches only in partReview; check for a typo
dobFailedFullothersDate of birth contradicts the recordCheck the date, then decline
lastNameOnly, firstNameOnlyothersOnly one name part matchedReview

The schema also lists skippedDOB; the service never sets it.

Errors

Error bodies are JSON. Their shape varies by cause.

StatusWhenBody
400A key is repeated in the JSON body{"status": false, "message": "Duplicate keys found: ..."}
401No bearer token{"status": false, "message": "Missing AuthorizationToken"}
403Account not enabled for the service{"status": false, "message": "Access denied: You cannot access this product. Please contact support."}
429Transaction limit used up{"status": false, "message": "Transaction limit exceeded", "limit": n, "remaining": 0}
500Source failed, or a required field is missing{"status": false, "error": "..."}

Pending verification: how an invalid or expired token is reported. The service can answer 500 with the token library's message instead of 401.

Sandbox

The sandbox does not call any source. It answers from nine fixed test cases and returns the same response shape. It is authenticated like production.

A request is matched on all of country, street, number, zip, city, the full name and dob, exactly as written. DEU, AUT and CHE are accepted for the country.

#CountryStreet and numberPost code, cityNameDate of birthResult
1ATKampgasse 93492 EtsdorfJoe Cardholder1970-12-01OK, 100, EXACT, unchanged
2ATBillrothstr. 39/161190 WienWilma Wohndtort1998-03-05OK, 95, EXACT, corrected to Billrothstraße 39/16
3ATBillrothstraße 391190 WienWilma Wohndtort1998-03-05OK, 80, HOUSENUMBER_MATCH
4ATBillrothstraße 3/161190 WienWilma Wohndtort1998-03-05REVIEW, 50, CITY_MATCH
5ATDöblinger Hauptstraße 4/11190 WienWilma Wohndtort1998-03-05REVIEW, 50, CITY_MATCH
6DEEdlinger Hauptstraße 12/712345 BerlinHans Dampf1998-03-05NOK, 25, NO_MATCH
7CHSeegasse 11000 Lachen/ZürichHubert Spion1901-01-01NOK, 0, NO_MATCH
8ATKampgasse 93496 EtsdorfJoe Cardholder1970-12-01OK, 95, EXACT, post code corrected to 3492
9ATKampgasse 93492 EtsdorfJane Cardholder1970-12-01OK, 80, HOUSEHOLD_MATCH

These are test records, not real customers. Send firstname and lastname as the two words of the name.

Differences from production:

  • extendedMessage holds the case description, for example Full match or No match, not a reason code.
  • addressStatus and the address lines come from the test case. The country in them is two letters (AT), not three.
  • A request that matches no case still returns 200, with {"success": false, "message": "Test Case Mismatch (Case ID: ...)"} listing which fields differ from the closest case. The message is a string, not the response shape above. Check for success before reading the fields. If nothing is close, the message starts with Critical Failure.
  • Only AT, DE and CH have test cases. There is no case for other countries, and none that returns HOUSEHOLD_MATCH or NO_MATCH through the international source.

Mock

This portal serves canned answers that need no token and no account, so you can build a client first. They are not the sandbox: the body is not computed from your request.

POST /mock/api/services/address/verify returns the OK example above. Add ?scenario=review for a REVIEW and ?scenario=no-match for a NOK. ?scenario=bad-request and ?scenario=unauthorized return the error shapes from the schema. See Environments.

Worked examples

OK

The request and response at the top of this page: Germany, address corrected, score 95.

REVIEW

Spain, answered by the international source. City and post code match, the street does not.

{
  "inputAddress": "Calle Ejemplo 5, 28001, Madrid, ES",
  "correctedAddress": "Calle Ejemplo 5, 28001, Madrid, ES",
  "finalAddress": "Calle Ejemplo 5, 28001, Madrid, ES",
  "addressStatus": "unchanged",
  "addressComponents": {
    "street": "Calle Ejemplo",
    "number": "5",
    "zip": "28001",
    "city": "Madrid",
    "country": "ES"
  },
  "matchQuality": "CITY_MATCH",
  "score": 50,
  "globalResult": { "overall": "REVIEW", "totalScore": 50 },
  "identity": { "fullName": "Juan Ejemplo", "dob": "1990-07-23" },
  "extendedMessage": "cityMatch"
}

NOK

Same request, but nothing matched.

{
  "inputAddress": "Calle Ejemplo 5, 28001, Madrid, ES",
  "correctedAddress": "Calle Ejemplo 5, 28001, Madrid, ES",
  "finalAddress": "Calle Ejemplo 5, 28001, Madrid, ES",
  "addressStatus": "unchanged",
  "addressComponents": {
    "street": "Calle Ejemplo",
    "number": "5",
    "zip": "28001",
    "city": "Madrid",
    "country": "ES"
  },
  "matchQuality": "NO_MATCH",
  "score": 0,
  "globalResult": { "overall": "NOK", "totalScore": 0 },
  "identity": { "fullName": "Juan Ejemplo", "dob": "1990-07-23" },
  "extendedMessage": "noMatch"
}

For a NOK from DE or AT, addressComponents can hold empty strings and finalAddress can be made of separators only, because no address was confirmed. Fall back to inputAddress.

Limits

  • Per-account choice of source and a second-source fallback are not available at present.
  • Pending verification: behaviour of the production routes against a live source. This page is written from the service code, not from live calls.

On this page