> ## Documentation Index
> Fetch the complete documentation index at: https://docs.voxworks.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth Check (Caller Identity Verification)

> Verify a caller's identity by comparing the details they give you against a reference record, with exact or semantic matching and a configurable pass threshold.

## What is Auth Check?

**Auth | Check** verifies that the person on the call is who they say they are. It compares two lists of values — the details the caller has given during the call, and the reference details you hold on record — position by position, and decides whether enough of them match.

Typical uses:

* Verifying a caller before reading out account, policy, or claim details
* Applying an "any 2 of 3 identifiers" policy (name, date of birth, phone number)
* Checking a caller against a set of records returned by your own system via a webhook

You add it to a script as a tool step in the flow editor (the **Script** tab of the script editor). It appears in the tool picker as **Auth | Check** and needs no connected integration. On every run it writes a full set of result variables in the `auth_check` namespace, so later steps can route on the outcome and your team can audit how the decision was made.

***

## How it works

The tool takes two parallel arrays:

* **Record Array** — the reference values you trust: fields from the contact record, values passed in when the call was created, or data returned by a webhook earlier in the call.
* **Check Array** — the values the caller provided, captured by earlier collection steps in the flow.

Comparison is strictly **by position**: the first Record Array value is compared with the first Check Array value, the second with the second, and so on. A missing or empty value on either side fails that position. The caller is verified when the number of matching positions reaches the **Required Match Count** — leave it blank and every position must match.

Both arrays must be the same length and in the same order. If one array is longer, the unpaired positions count as failed. Adding a value to one array and not the other shifts every later comparison, and nothing warns you at save time — the check simply produces wrong results at runtime.

***

## Settings

Record Array, Check Array, Check Transcript, and Raw Data each accept a literal value or a variable reference; type `/` in Record Array, Check Array, or Check Transcript to open the variable picker. **Required Match Count** is a plain number field and **Exact** is a Yes/No switch — both take a literal value only, with no variable option.

| Setting              | Description                                                                                                                                                                                                                                                                                           | Default                          |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| Record Array         | Required. Reference values, compared by position. Usually variable references such as `{{contact.full_name}}` (string, e.g. `Jane Citizen`).                                                                                                                                                          | —                                |
| Check Array          | Required. Caller-provided values, compared by position. Usually variable references such as `{{custom.caller_name}}` (string, e.g. `Jane Citizen`). Must align position-for-position with the Record Array.                                                                                           | —                                |
| Required Match Count | Minimum number of matching positions for the check to pass. Must be a whole number; anything else fails the check with status `error`.                                                                                                                                                                | Blank = all positions must match |
| Exact                | Yes (or blank) = exact matching only. No = semantic matching is also allowed — see below.                                                                                                                                                                                                             | Yes (exact matching)             |
| Check Transcript     | An array of `"true"`/`"false"` strings, one per Check Array position. Positions set to `"true"` are re-extracted from the live transcript before comparing — see [Refreshing values from the transcript](#refreshing-values-from-the-transcript). Must be exactly the same length as the Check Array. | No refresh                       |
| Raw Data             | A JSON record set to verify against instead of a single record — see [Verifying against a set of records](#verifying-against-a-set-of-records). Typically `{{webhook.response}}` (string). Used together with field-path references in the Record Array.                                              | Not set                          |

***

## Exact vs semantic matching

**Exact matching** (the default) is deterministic but forgiving of formatting: differences in case, spacing, commas, and full stops are ignored. Values that look like dates are normalised to a standard `YYYY-MM-DD` form on both sides before comparing, so a record value of `1985-03-12` matches a caller value captured as `12/03/1985`. Keep your reference dates of birth in `YYYY-MM-DD` so there is no day/month ambiguity.

**Semantic matching** (set **Exact** to No) runs only when the exact comparison fails, and tolerates close spelling and transcription variants of the same value — useful for names and addresses captured over the phone. The two values must start with the same letter and be nearly identical in spelling and length:

* `Steven` matches `Stephen` — a close spelling variant
* `Bob` does **not** match `Robert` — nicknames and abbreviations never match; collect what the record actually stores
* Date values never match semantically — they only pass the exact comparison

Which path decided each outcome is recorded in `{{auth_check.method}}` (string, e.g. `mixed`) and the `exact_match` / `semantic_match` flags, so you can audit or route differently when a semantic match carried the decision.

***

## Refreshing values from the transcript

Callers correct themselves — "sorry, it's spelt S-T-E-P-H-E-N" — after your collection step has already stored the first attempt. The **Check Transcript** setting handles this: for each position flagged `"true"`, the tool re-extracts that value from the conversation transcript at check time instead of trusting the stored capture, and writes the refreshed value back into the referenced variable, so `{{custom.caller_dob}}` (string, e.g. `1985-03-12`) holds the corrected value afterwards. If the transcript offers nothing clearer for a position, the stored value is kept.

Two rules:

* The array holds the **strings** `"true"` and `"false"`, one entry per Check Array position — for example `["true", "true", "false"]`.
* It must be exactly the same length as the Check Array. A length mismatch fails the whole check with `{{auth_check.status}}` (string) set to `error`.

***

## Verifying against a set of records

When the caller could match one of several records — for example, a lookup that returns every policy under a phone number — the tool can test the caller's answers against each record in the set:

1. Store the record set earlier in the flow, typically with a Send Webhook step that writes to `{{webhook.response}}` (string, e.g. `[{"name": "Jane Doe", "date_of_birth": "1985-03-12"}, {"name": "John Doe", "date_of_birth": "1990-07-01"}]`).
2. In the **Record Array**, point each entry at a field *inside* the records by adding the field name after the variable — `{{webhook.response}}.name`, `{{webhook.response}}.date_of_birth`. Entries still line up position-for-position with the Check Array.
3. Supply the same record set in **Raw Data** — the records written out on a pass take this shape.

The tool checks every record in the set against the caller's values. The step passes when at least one record verifies, and the passing records are written to `{{auth_check.verified_data}}` (string, e.g. `[{"name": "Jane Doe", "date_of_birth": "1985-03-12"}]`) for later steps to read. When exactly one record was checked and it passed, the variable holds that record on its own rather than a one-record list.

Plain variable references such as `{{contact.full_name}}` in the Record Array make the tool run an ordinary single-record check — Raw Data is ignored in that case. Use the field-path form above whenever you want the record-set behaviour.

***

## Variables this tool writes

Auth | Check writes the following variables on every run — including failed checks — immediately after the step executes.

| Variable                        | Type    | Example                                        | Description                                                                                                |
| ------------------------------- | ------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `{{auth_check.verified}}`       | boolean | `true`                                         | Overall verification outcome.                                                                              |
| `{{auth_check.status}}`         | string  | `verified`                                     | `verified`, `failed`, `insufficient_data` (an empty Record or Check Array), or `error` (invalid settings). |
| `{{auth_check.matched_count}}`  | integer | `2`                                            | How many positions matched.                                                                                |
| `{{auth_check.required_count}}` | integer | `2`                                            | The threshold that applied on this run.                                                                    |
| `{{auth_check.matched_checks}}` | json    | `["0", "1"]`                                   | The positions (as strings, counting from `"0"`) that matched.                                              |
| `{{auth_check.failed_checks}}`  | json    | `["2"]`                                        | The positions that failed.                                                                                 |
| `{{auth_check.exact_match}}`    | boolean | `true`                                         | True when the caller verified without any semantic match being needed.                                     |
| `{{auth_check.semantic_match}}` | boolean | `false`                                        | True when at least one position matched semantically.                                                      |
| `{{auth_check.method}}`         | string  | `exact`                                        | `exact`, `semantic`, or `mixed` — which matching path produced the result.                                 |
| `{{auth_check.notes}}`          | string  | `2 of 2 required checks matched`               | Explanatory note about the outcome.                                                                        |
| `{{auth_check.verified_data}}`  | string  | `[{"policy": "POL-1234", "name": "Jane Doe"}]` | Passing records when verifying against a record set; empty otherwise.                                      |

The `auth_check` namespace belongs to the tool — read from it freely, but never write into it yourself. Each run also adds a summary and per-position results to the call's **System Notes**, visible on the call detail page in the Call Log.

***

## Routing the result

The tool's single output is a boolean **result**:

* **true** — the required match count was met (or, with Raw Data, at least one record verified). Route to the protected part of your flow.
* **otherwise** — the caller was not verified. Route to a retry, a fallback, or a human handoff.

For graduated handling — say, the caller matched one identifier of three — follow the step with a Code Step that branches on `{{auth_check.matched_count}}` (integer, e.g. `1`) or `{{auth_check.failed_checks}}` (json, e.g. `["1", "2"]`), rather than re-running the check blindly.

***

## Example verification flows

### Any 2 of 3 identifiers, with semantic name matching

Earlier steps collect the caller's name, date of birth, and phone number into custom variables. The check passes if any two match, tolerates a transcription variant of the name, and refreshes the name and date of birth from the transcript in case the caller corrected themselves:

```text theme={null}
Record Array:          {{contact.full_name}}, {{contact.date_of_birth}}, {{contact.phone}}
Check Array:           {{custom.caller_name}}, {{custom.caller_dob}}, {{custom.caller_phone}}
Required Match Count:  2
Exact:                 false
Check Transcript:      ["true", "true", "false"]
```

Route **true** to the account-details step; route **otherwise** to a step that apologises and offers a transfer to a human.

### Strict all-match check

For higher-risk actions, leave **Required Match Count** blank (all must match) and **Exact** set to Yes (or blank) so only exact matches count:

```text theme={null}
Record Array:          {{contact.date_of_birth}}, {{contact.phone}}
Check Array:           {{custom.caller_dob}}, {{custom.caller_phone}}
```

### Verify against records from your own system

A Send Webhook step earlier in the flow looks up the caller's phone number and stores the matching records in `{{webhook.response}}` (string). Auth | Check then tests the caller's answers against the `name` and `date_of_birth` fields of each record:

```text theme={null}
Record Array:          {{webhook.response}}.name, {{webhook.response}}.date_of_birth
Check Array:           {{custom.caller_name}}, {{custom.caller_dob}}
Required Match Count:  2
Raw Data:              {{webhook.response}}
```

On **true**, read the matching record from `{{auth_check.verified_data}}` (string, e.g. `{"name": "Jane Doe", "date_of_birth": "1985-03-12"}`) in the following steps.

***

## Common pitfalls

* **Misaligned arrays** — the Record and Check Arrays are compared by position, and nothing validates that they line up. Keep them the same length and order.
* **Blank Required Match Count is the strictest setting** — it means *all* positions must match, not a sensible middle ground. Set it explicitly for "N of M" policies.
* **Blank Exact means exact** — semantic matching is opt-in. Set **Exact** to No when comparing names or addresses captured by voice.
* **Check Transcript length must match** — one `"true"`/`"false"` string per Check Array position, or the whole check fails with status `error`.
* **Semantic matching is not a nickname resolver** — it covers close spellings only. If the record stores `Robert`, ask for the name as it appears on the account.
* **Record-set checks need field paths** — plain variable references in the Record Array run a single-record check and ignore Raw Data. Use the `{{webhook.response}}.field_name` form to check against every record in a set.
* **Date formats** — store reference dates of birth as `YYYY-MM-DD` to avoid day/month ambiguity in normalisation.

***

## Next Steps

* [Tool Steps](/flows/steps-tool) — How to add and configure tool steps in the flow editor.
* [Variables](/flows/variables) — How variable references work across scripts and tools.
* [Send Webhook](/tools/webhook) — Fetch reference records from your own systems to verify against.
* [Warm Handoff](/tools/warm-handoff) — Hand failed verifications to a human agent.
