Browse guides

Use cases

Identity verification (KYC-light)

The most common use case: confirm a person exists with the supplied first name, last name, and date of birth. Often used as a low-touch identity check on its own, or as a pre-screen before launching a full DVS document verification.

Request:

{
  "first_name": "John",
  "middle_name": "Andrew",
  "last_name": "Smith",
  "birth_date": "1998-03-21"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "middle_name": "match",
      "last_name": "match",
      "dob": "match"
    }
  ]
}

What this tells you: there is a person named John Andrew Smith with date of birth 21 March 1998 in our universe. All four supplied fields matched cleanly. If the user had got the DOB wrong, dob would come back as no_match (or match_year / match_day_month_reversal if it was close - see the DOB partial matches example).

Phone tied to person (signup confirmation)

During signup, you've collected a name and a phone number from the user. You want to confirm that the phone number is actually associated with that person in our universe before trusting it for SMS-based 2FA or password recovery.

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "phone": "0399999999"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "phone": "match"
    }
  ]
}

If the phone did not belong to a John Smith on file, the best candidate would still come back, but with phone: no_match. That means the name was found but the phone is not associated with it - a useful signal for fraud-prevention workflows.

Phone numbers are accepted in either Australian 0NSN form (10 digits starting with 0) or the equivalent E.164 form (+61 followed by 9 digits). The leading + on the E.164 form is optional - 61400123456 is treated the same as +61400123456, which avoids spurious rejections of numbers that have round-tripped through Excel / CSV imports that silently strip the +. All three shapes are normalised internally before comparison, so it does not matter which one your application stores.

Email tied to person (signup confirmation)

The same pattern as the phone use case, but using an email address instead.

Request:

{
  "first_name": "Mary",
  "last_name": "Jones",
  "email": "mary_jones89@example.com"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "email": "match"
    }
  ]
}

Multi-contact verification

Customers often have several contact points on file (current and old phone numbers, work and personal email addresses). You can supply up to four phones (phone, phone2, phone3, phone4) and up to four emails (email, email2, email3, email4) on a single request.

Each slot is evaluated independently against the candidate's full set of phones / emails, so the response tells you exactly which of your supplied values matched. Slots you did not supply are omitted from the row.

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "birth_date": "1998-03-21",
  "phone": "0399999999",
  "phone3": "0491222111",
  "email": "jsmith1998@example.com"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "dob": "match",
      "phone": "match",
      "phone3": "match",
      "email": "match"
    }
  ]
}

Notice that the request used phone and phone3 (skipping phone2); the response mirrors that and contains a phone outcome and a phone3 outcome with no phone2 or phone4 keys. The order does not matter for matching - each slot is checked against the candidate's complete phones list - so it is fine to use whichever slots best line up with how your application stores its data.

If a slot does not match (for example, the customer gave you an old phone number that is no longer on file), that slot will return no_match while the other slots can still return match.

Address verification (full match)

Confirm that a person lives at a specific address. Address can be supplied in any one of three forms (mutually exclusive):

  • Address parts - street_address + suburb + state + postcode, all required together.
  • Full address - a single full_address string that we parse for you.
  • GNAF id - the canonical gnaf_id for the property.

Whichever form you use, the address is resolved to its canonical components before evaluation, so the same address rollup logic applies.

Using address parts

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "street_address": "20 Hardy St",
  "suburb": "Lilydale",
  "state": "VIC",
  "postcode": "3140"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "address": "match",
      "street_address": "match",
      "suburb": "match",
      "state": "match",
      "postcode": "match"
    }
  ]
}

Using full_address

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "full_address": "20 Hardy St, Lilydale VIC 3140"
}

The response shape is identical - the full_address is parsed and resolved to the same canonical components, then matched against the candidate.

Using gnaf_id

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "gnaf_id": "GAVIC421647320"
}

Same response shape. Use this form when you already hold the canonical GNAF id (for example from a prior address validation call).

GNAF persistent identifiers come in two shapes depending on the state of issue. Some states (ACT, NSW, VIC, QLD, WA, TAS) emit them with no separator between the state prefix and the digit run (GAVIC421647320, GANSW710277749). NT and SA emit them with a single underscore between the prefix and the digits (GANT_717247498, GASA_414917272). Both forms are accepted - keep the underscore as it appears in the source id rather than pre-stripping it.

Partial address lookup

Sometimes you only have part of an address, or the user has supplied a slightly different address than what is on file. The address rollup tells you at a glance how good the match is, and the four component fields tell you exactly which parts agreed.

"Same street, different suburb" - match_street

Suppose your records show John Smith at 20 Hardy Street, Doncaster VIC 3108 but our universe has him at Lilydale 3140. The street number and name match, but everything else is wrong.

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "street_address": "20 Hardy St",
  "suburb": "Doncaster",
  "state": "VIC",
  "postcode": "3108"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "address": "match_street",
      "street_address": "match",
      "suburb": "no_match",
      "state": "match",
      "postcode": "no_match"
    }
  ]
}

The rollup match_street says "we found someone of this name on the same street, but not at the same locality" - useful for nudging a user to double-check their address details rather than rejecting outright.

"Is there a John Smith in postcode 3140?" - match_locality

If you only know the locality, supply just the suburb / state / postcode. Even with a placeholder street address, the rollup will tell you whether anyone of that name lives in that locality.

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "street_address": "1 Unknown St",
  "suburb": "Lilydale",
  "state": "VIC",
  "postcode": "3140"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "address": "match_locality",
      "street_address": "no_match",
      "suburb": "match",
      "state": "match",
      "postcode": "match"
    }
  ]
}

match_locality requires state to match (when supplied) and at least one of suburb or postcode to also match. Use this when you want a "yes there's a John Smith in this area" confidence signal rather than an exact-address check.

Multi-candidate review

Set return_multiple_candidates: true to receive up to 5 candidates ordered best-first.

When to use it

Default single-candidate mode is the right choice for high-confidence yes/no checks: "is this person in the universe?", "does this phone belong to this person?". The single best candidate carries the per-field outcomes you need to drive an auto-approve / manual-review / reject decision, and the response stays small and easy to consume.

Switch to multi-candidate mode when one of the following applies:

  • Building a manual review queue. Reviewers benefit from seeing several plausible candidates side-by-side so they can pick the right one rather than blindly trusting the top pick.
  • Building a "did you mean...?" disambiguation step. When a user has typed something that could match more than one record (a common surname with a nickname, a postcode-only locality lookup), surfacing the alternates lets the user resolve the ambiguity themselves.
  • Hedging against ranker disagreement. When the request supplies several identifiers that point in different directions (the user's phone belongs to Person A, but their name and DOB match Person B), the top candidate reflects the ranker's calibration. Multi-candidate mode lets you see both Person A and Person B and apply your own business rules to choose between them.
  • Investigating an unexpected outcome. When a downstream workflow flagged a record and you want to understand "why didn't the right person win?", inspecting the full top-5 is the fastest way to see what else was considered.
  • Auditing or sampling. When validating a new data feed or comparing universe coverage against an internal source, seeing all distinct candidates per query gives a clearer picture than the single best.

For straight pass/fail verification flows, leave it off - the single-candidate response is faster to handle and conveys the same yes/no signal.

Response shape and dedupe behaviour

match_results always returns one row per matched person - it never duplicates the same person once per address on file. A person with three known addresses still appears as a single row, with the address outcome reflecting the strongest match across all of them (see Address above for the best-address selection rules).

A subtle but important behaviour: identical rows are collapsed. If two underlying universe records produce the same per-field outcome map against your request (for example, two Roberts at the same address with the same DOB, both evaluated against your supplied first_name + last_name + birth_date), they appear in the response as a single row, not two. The number of rows in match_results therefore depends on how many distinct outcome maps your request produced, not on how many universe records were considered.

The practical implication: a request like { "last_name": "Brown", "return_multiple_candidates": true } returns just a single row with last_name: match, because every Brown candidate evaluates identically against a request that only supplies a last name. The response cannot tell you how many Browns are in the universe; it can only tell you that at least one exists.

To get multiple distinct rows, supply additional identifiers so that the candidates differ in their outcomes. For example, supplying a first_name lets candidates with different first names produce different first_name outcomes (match vs no_match), so they appear as separate rows.

Request:

{
  "first_name": "Robert",
  "last_name": "Brown",
  "first_name_matching": ["exact", "alias", "partial"],
  "return_multiple_candidates": true
}

Response (the sandbox has Robert Patrick Brown, a Robert Brown with no DOB, and an R BROWN record):

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match"
    },
    {
      "first_name": "partial_match",
      "last_name": "match"
    }
  ]
}

The two Robert Brown records (with and without a DOB on file) collapse into the first row because they evaluate identically against this request; the R BROWN record produces a distinct second row because its first name only matches partially. Each row in match_results is independent and self-describing, so you can route them straight into a review UI.

Typo tolerance with fuzzy matching

When your data may contain spelling variations of names (Mathew/Matthew, Smith/Smyth, Stephen/Steven), opt in to fuzzy matching for the affected name field. Fuzzy matching uses a phonetic (metaphone) algorithm and additionally requires the same leading letter, so it is robust against typos while still being conservative.

Request:

{
  "first_name": "Mathew",
  "last_name": "Smith",
  "birth_date": "1999-10-21",
  "first_name_matching": ["exact", "fuzzy"]
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "fuzzy_match",
      "last_name": "match",
      "dob": "match"
    }
  ]
}

fuzzy_match was returned for first_name because Mathew and Matthew share the same metaphone code (M0) and start with the same letter. The DOB and last name still matched exactly, so this is a strong overall match across all three supplied identifiers.

To allow spelling variations on the last name as well, add last_name_matching: ["exact", "fuzzy"]. This also widens the underlying candidate search so that records with a fuzzy last name are considered, not just records with the exact supplied last name.

Nickname / alias tolerance

Customers may identify themselves by their preferred name (Bob, Jenny, Tony) when their record is on file under a formal name (Robert, Jennifer, Antonio). Opt in to alias matching to allow the comparison.

Request:

{
  "first_name": "Bob",
  "last_name": "Brown",
  "birth_date": "1971-03-18",
  "first_name_matching": ["exact", "alias"]
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "alias_match",
      "last_name": "match",
      "dob": "match"
    }
  ]
}

The alias group catalogue includes hundreds of common nickname pairings. You can combine ["exact", "alias", "fuzzy"] to tolerate both nicknames and typos, or add "partial" to also accept single-initial matches like "R" -> "Robert".

DOB partial matches

Date of birth has its own ladder of partial outcomes for cases where the customer has the year right but not the exact date.

Day / month reversal

Mary Jones is on file with a DOB of 12 August 1989 (1989-08-12 in YYYY-MM-DD). Upstream systems frequently mix up the regional date conventions DD/MM/YYYY and MM/DD/YYYY. For example, a customer who handwrites 12/08/1989 on a form (intending 12 August) might have it entered into a US-style system as 8 December 1989 - the day and month silently swap. When that incorrect date later makes its way into a request to this API, the supplied value is 1989-12-08 instead of 1989-08-12. The check spots that the year matches and the day and month are swapped, and reports the outcome explicitly.

The API itself always requires birth_date in YYYY-MM-DD format - the day/month confusion described above happens upstream of the API, in the system that originally collected the date. This check just helps you recover when that has already happened.

Request:

{
  "first_name": "Mary",
  "last_name": "Jones",
  "birth_date": "1989-12-08"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "dob": "match_day_month_reversal"
    }
  ]
}

Year-only

If only the year matches but the day and month do not (and they are not a reversal), the response returns match_year:

Request:

{
  "first_name": "Mary",
  "last_name": "Jones",
  "birth_date": "1989-05-15"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "dob": "match_year"
    }
  ]
}

No DOB on file

Some universe records have no DOB on file at all. When that is the case, the candidate returns dob: no_record rather than no_match - a useful distinction because it tells you "we matched this person, but we have no DOB to compare against" instead of "we matched this person and their DOB is different to yours".

The sandbox includes a Robert Brown record with phone 0491222333 and no DOB on file (distinct from the Robert Patrick Brown record born 1971-03-18). Searching for him by phone, with any DOB, shows the outcome:

Request:

{
  "first_name": "Robert",
  "last_name": "Brown",
  "phone": "0491222333",
  "birth_date": "1985-06-04"
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "dob": "no_record",
      "phone": "match"
    }
  ]
}

dob: no_record is a signal that the candidate is on file but does not have a date of birth recorded, so the supplied DOB cannot be confirmed or refuted from our universe. It is not a positive match, but it is also not a no_match either - decide per workflow whether the absence of a DOB on file is acceptable, or whether you want to fall back to another verification path.

If your supplied DOB happens to match a different candidate (for example, supplying 1971-03-18 would match the Robert Patrick Brown record), that other candidate's dob: match will outscore this candidate's dob: no_record and become the top result. Set return_multiple_candidates: true if you want to see both rows.

The other partial DOB outcomes (match_year, match_day_month_reversal) are useful when DOB is collected from messy sources (handwritten forms, customer entry on a phone keypad).

Dirty input with ignore_errors

When you are checking data collected from imperfect sources - a customer-facing signup form, a CSV import, a third-party feed - some individual fields will inevitably be malformed. By default, an invalid phone number or DOB will cause the whole call to fail with a 400. With ignore_errors: true, the endpoint silently drops any individual field that fails validation and runs the match against whatever valid input remains. Dropped fields come back in the response as not_used so you can see exactly what was ignored.

Request:

{
  "first_name": "John",
  "last_name": "Smith",
  "birth_date": "1987666",
  "phone": "1234",
  "email": "jsmith1998@example.com",
  "ignore_errors": true
}

Response:

{
  "message": "Ok",
  "api_reference": "fe4291ca-d831-4760-96df-c9cb03b3cd95",
  "match_results": [
    {
      "first_name": "match",
      "last_name": "match",
      "dob": "not_used",
      "phone": "not_used",
      "email": "match"
    }
  ]
}

Use ignore_errors: true whenever you cannot guarantee the cleanliness of the inputs and you'd rather get a partial result than re-architect a retry-with-bad-fields-omitted loop on your side. Note that the request still has to be structurally well-formed (correct types, last_name present); only individual field-value problems are soft-ignored.

Confidence-based routing

Each candidate row reports the per-field outcomes directly. To drive a downstream routing rule, branch on the specific outcomes that matter for your use case rather than on a single aggregate score. For example:

  • exact last_name plus exact dob plus any phone match → auto-approve.
  • exact last_name plus a partial DOB outcome (match_day_month_reversal / match_year) → manual review queue.
  • everything else → reject or trigger a more thorough verification (such as a DVS document check or an ID Pass).

The exact thresholds depend on your risk appetite and the field mix you are sending; the Best-candidate selection table tells you how strongly each outcome weighs in the candidate ranking, which is a useful starting point when calibrating.