Contact Match
Overview
Contact Match compares the identity attributes you already have against the identity Trustfull can resolve, and returns one status per attribute. It is a comparison product, not a decision product: the response tells you where your data agrees with our raw data, field by field, and your own policy decides what to do with that pattern.
Three properties define the contract:
- Statuses, never raw data. Each result carries a field name and a status. Trustfull never returns its raw data for a field, so a mismatch tells you that you disagree, not what the alternative is.
- No score and no verdict. There is no aggregate number in the payload. Counting and weighting the statuses is your decision.
- Never wider than your request. Attributes you do not send are absent from the response.
Limited availability. Contact Match runs on a dedicated endpoint with its own API keys, separate from the main Trustfull API. Contact your account team to be enabled. Need help? See Getting Support.
Authentication
All requests require an x-api-key header carrying the key issued for Contact Match.
x-api-key: your-api-key-here
Keep your API key secure. Do not expose it in client-side code, public repositories, or browser requests. All calls must be made from your backend.
Endpoint
Contact Match uses a dedicated host rather than api.fido.id. Every comparison is a POST to a single endpoint:
POST https://kyc.trustfull.com/v1/verify
Request Format
Every attribute belongs to a single DataFields object, grouped by attribute type. DataFields is the only required key: every group inside it, and every field inside a group, is optional. Send only the attributes you actually have, because an absent field is never treated as a mismatch.
{
"DataFields": {
"PersonInfo": {
"FirstGivenName": "John",
"MiddleName": "Henry",
"FirstSurName": "Smith",
"DayOfBirth": "5",
"MonthOfBirth": "3",
"YearOfBirth": "1983",
"Gender": "M"
},
"Location": {
"BuildingNumber": "10",
"UnitNumber": "3",
"StreetName": "Lawford",
"StreetType": "st",
"City": "Milano",
"Suburb": "Doncaster",
"StateProvinceCode": "MI",
"PostalCode": "20121"
},
"Communication": {
"MobileNumber": "393401234567"
}
}
}Three formatting rules affect your results:
- Case is irrelevant. Every comparison upper-cases both sides first.
- Date parts are whole numbers.
"5"and5are equivalent, and an unpadded"3"is correct for March. MobileNumberis matched as an exact string. Send the country code with no separators, spaces, or leading+. A differently formatted number produces a formatting mismatch, not an identity mismatch.
curl -X POST https://kyc.trustfull.com/v1/verify \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key-here" \
-d '{
"DataFields": {
"PersonInfo": {
"FirstGivenName": "John",
"FirstSurName": "Smith",
"DayOfBirth": "5",
"MonthOfBirth": "3",
"YearOfBirth": "1983"
},
"Communication": {
"MobileNumber": "393401234567"
}
}
}'Supported Fields
Sixteen attributes are compared and can appear in the response. Each is compared in one of two ways:
- Exact: case-insensitive whole-string equality. An exact field never returns
partial_match. - Fuzzy: a bounded number of character differences is tolerated. Only fuzzy fields can return
partial_match.
A key field also helps Trustfull resolve the person in the first place. See What Makes a Lookup Possible.
PersonInfo
| Field | Type | Compared | Notes |
|---|---|---|---|
FirstGivenName | string | Fuzzy | Key. Given name. |
MiddleName | string | Fuzzy | Compared only when you send it. |
FirstSurName | string | Fuzzy | Key. Note the capital N in the field name. |
DayOfBirth | number | Exact | Key. 1-31, unpadded. |
MonthOfBirth | number | Exact | Key. 1-12, unpadded. |
YearOfBirth | number | Exact | Key. Four digits. |
Gender | string | Exact | Compared as sent, for example M or F. |
Location
| Field | Type | Compared | Notes |
|---|---|---|---|
BuildingNumber | string | Exact | Street number. |
UnitNumber | string | Exact | Flat or apartment number. |
StreetName | string | Fuzzy | Name only. Keep the type in StreetType. |
StreetType | string | Exact | Via, Corso, St, Rd. |
City | string | Exact | Exact, so send the official place name. |
Suburb | string | Fuzzy | Subdivision or municipality. |
StateProvinceCode | string | Exact | Province or state code. |
PostalCode | string | Exact | ZIP or CAP. |
Communication
| Field | Type | Compared | Notes |
|---|---|---|---|
MobileNumber | string | Exact | Key. The strongest resolution key. |
What Makes a Lookup Possible
Before anything can be compared, Trustfull has to resolve the person. The two routes below are tried in order and the first one that resolves wins, so this list is a precedence and not a menu. Meeting at least one route is the difference between a useful response and a response of all missing.
Communication.MobileNumber- the strongest route. A mobile number on its own resolves an identity, and sending it materially improves the fill rate of every other field.FirstGivenName+FirstSurName+DayOfBirth+MonthOfBirth+YearOfBirth- name plus a complete date of birth. All five must be present, so a year-only or month-and-year date does not qualify.
When no route resolves, the call still returns
200. Every attribute you sent comes back asmissing. Read that as "we could not see this person", not as "this person is wrong": an all-missingresponse carries no evidence in either direction.
Sending a mobile number together with name and full date of birth is the most reliable request you can make. The number resolves the identity, and each remaining attribute is then compared on its own.
Response Format
A successful call returns 200 with one flat array of results. Order is stable (person attributes, then location, then mobile number), but key on FieldName rather than on position.
{
"DatasourceResults": {
"DatasourceFields": [
{ "FieldName": "FirstGivenName", "Status": "partial_match" },
{ "FieldName": "MiddleName", "Status": "missing" },
{ "FieldName": "FirstSurName", "Status": "match" },
{ "FieldName": "MonthOfBirth", "Status": "match" },
{ "FieldName": "YearOfBirth", "Status": "no_match" },
{ "FieldName": "Gender", "Status": "match" },
{ "FieldName": "StreetName", "Status": "partial_match" },
{ "FieldName": "PostalCode", "Status": "match" },
{ "FieldName": "MobileNumber", "Status": "match" }
]
}
}One rule governs the contents of the array, and it means the array can be shorter than you expect:
- Attributes you did not send are omitted entirely. There is no
not_providedstatus in the payload, so silence means you never asked. In the example above,DayOfBirthis absent because the request did not include it.
Status values are always lower case.
The Four Statuses
These are four distinct outcomes rather than a scale. In evidential terms missing and no_match are opposites: one is an absence of information, the other is a contradiction.
| Status | Meaning |
|---|---|
match | Your value and our raw data are both present and identical, ignoring case. On a fuzzy field this means zero character differences. |
partial_match | Your value and our raw data are both present and not identical, but the difference falls inside the tolerance for that field's length. Only fuzzy fields return this. |
no_match | Your value and our raw data are both present and disagree beyond tolerance. This is positive evidence of a discrepancy. |
missing | You sent the attribute but Trustfull has no raw data for it, either because the resolved identity is not populated for that attribute or because no identity could be resolved. It is never a statement about your value. |
Because missing covers both "attribute not available" and "person not found", treat an all-missing response as no signal rather than a negative one. A response that mixes match with a single no_match is the informative case.
How Partial Match Is Decided
Five fields tolerate a small difference between your value and our raw data: FirstGivenName, MiddleName, FirstSurName, StreetName, and Suburb. Roughly one character in five may differ, so a typo, an accent, or a transliteration variant still returns partial_match. Case never matters anywhere.
Every other compared field is matched exactly and returns only match, no_match, or missing. That includes dates, gender, building and unit numbers, street type, city, state or province code, postal code, and mobile number.
One consequence is worth planning for: your value is compared only once Trustfull also has raw data for that field. Absent raw data always reads as missing and never as no_match.
Do not read
partial_matchas "weak match". It means your value and our raw data differ by an amount consistent with a transliteration, a typo, or a diacritic that survived differently on each side. Most integrators count it withmatchon name fields and review it separately on address fields, where a single difference can point to a different street. That choice is yours to tune.
Worked Comparisons
| You send | Our raw data | Result |
|---|---|---|
john | JOHN | match |
John | JOHNS | partial_match |
Smith | SMYTH | partial_match |
Lawford | LAWFOR | partial_match |
Maria-Luisa | MARIALUISA | partial_match |
Li | LU | no_match |
Rossi | VERDI | no_match |
Milano | MILAN | no_match |
Henry | No raw data | missing |
Errors
| Code | Body | When |
|---|---|---|
200 | DatasourceResults | Comparison completed. Also returned when no identity could be resolved, in which case every status is missing. |
403 | {"status":"ko","error":"missing x-api-key"} | No x-api-key header on the request. |
403 | {"status":"ko","error":"auth failed"} | Key not recognised, or not enabled for Contact Match. |
500 | {"status":"ko","error":"internal error"} | Includes a malformed body and a payload with no DataFields object. Validate your payload shape before retrying. |
Integration Notes
- Retries are safe. The call has no side effects you can observe, and an identical request returns identical statuses.
- Set a client timeout you can live with. Budget a few seconds, and fail open or closed according to your own policy.
- Send the widest payload you legitimately have. Unmatched fields cost you nothing because they are omitted rather than penalised, and every extra attribute is another independent piece of evidence.
- Key on
FieldNameand tolerate unknown names. Comparable attributes are added over time, and a parser that fails on an unrecognised field name will break on a future release.
FAQ
Do you return your raw data when a field does not match?No. Every result is a field name and a status. Trustfull never returns its raw data, so
no_matchtells you that your value and our raw data disagree, and nothing about what our raw data contains.
Every field came back asmissing. What went wrong?Nothing failed: the call succeeded, but no identity could be resolved from what you sent. Check the request against What Makes a Lookup Possible, and remember that an all-
missingresponse is an absence of evidence rather than a negative result.
Should I treatpartial_matchas a pass or a fail?That is your policy decision. Most integrators count it as a pass on name fields, where it usually reflects a spelling or transliteration variant, and review it separately on address fields, where a small difference can point to a different street.
Why isCityano_matchwhen my value is nearly right?
Cityis compared exactly, soMilanoandMILANdisagree. OnlyFirstGivenName,MiddleName,FirstSurName,StreetName, andSuburballow a near value. Send official place names rather than colloquial or anglicised ones.
I sent a field but it is not in the response. Why?The most likely cause is a spelling difference in the key name, so the field never arrived.
FirstSurNamecarries a capitalN, and that one catches most integrations. Check the request against Supported Fields.
Updated about 2 months ago