TrustWixDocs
Core concepts

Checks

The building blocks you compose, and what each one answers

A check is one question about a person. Flows are built from them, and a verification carries one check object per step, each with its own status, verdict and reason codes.

The check types

TypeThe question it answers
livenessIs a real, present human in front of the camera right now
face_matchIs the person in front of the camera the same person as on the document
documentIs this identity document genuine, unaltered and readable
age_estimationIs this person inside the age range you accept
aml_screeningDoes this identity appear on sanctions, watchlist or politically exposed person data
proof_of_addressDoes this document evidence the address the person claims

Age rules

The age_estimation step decides on the date of birth printed on the applicant's document, never on their face. A face-based estimate is carried alongside as a witness and it is never on its own a reason to refuse anybody: our own measurement puts single-frame error at around twelve years, and it over-ages minors far enough that most of them would clear a naive eighteen-plus gate. If your flow reads a document, the printed date is what your age rules are applied to.

Set the rules on the step's config.ageRules. Each rule is an inclusive age range in completed years plus an action, either reject or manual_review. Rules are evaluated in the order you send them, the first rule containing the applicant's age decides, and an age matching no rule passes. There is no approve action, because an age matching nothing already passes and an explicit approve would only ever be a way to shadow a stricter rule further down.

A minimum age is the rule that carries only an upper bound, because the people it acts on are the ones below it: "at least 18" is { "toAge": 17 }, with no fromAge. A maximum age is the mirror image, only a lower bound, because the people it acts on are the ones above it: "at most 75" is { "fromAge": 76 }, with no toAge. A band in between carries both. That is one mechanism rather than three, so a policy reads the same way whichever end it is about:

{
  "type": "age_estimation",
  "required": true,
  "config": {
    "ageRules": [
      { "toAge": 17, "action": "reject" },
      { "fromAge": 18, "toAge": 20, "action": "manual_review" },
      { "fromAge": 76, "action": "reject" }
    ],
    "ageRuleFallback": "pass"
  }
}

That flow refuses anyone under eighteen, sends eighteen to twenty to a reviewer, passes twenty-one to seventy-five, and refuses seventy-six and over. Both bounds are inclusive, so a twenty-year-old is inside the review band and a twenty-one-year-old is not. Rules must not overlap, and a request whose rules cover the same age twice is refused with a 400.

ageRuleFallback says what happens when no date of birth could be read, which includes a flow that reads no document at all. pass, the default, leaves the check deciding on the estimate exactly as a step with no age rules would. review sends the session to a human instead, on the grounds that a flow which states an age requirement and could not evaluate it has not answered its own question. Neither setting can refuse anybody, because a refusal on age needs a printed date behind it.

When a rule fires, the check carries AGE_REQUIREMENT_MISMATCH on a rejection and AGE_OUTSIDE_ACCEPTED_RANGE on a review. A session held because the age could not be determined carries AGE_NOT_DETERMINED.

Steps that are not checks

A flow step is usually a check, and one is not. phone asks the applicant for their phone number and stores it with the verification. It answers no question, so it produces no check object, carries no verdict and no score, and is never metered. It composes into a flow exactly like the types above and appears in the applicant's flow in the position you put it.

Because it decides nothing, a flow needs at least one real check alongside it. A flow made only of phone is refused when you save it.

The number is normalised to E.164 and encrypted at rest with the rest of the applicant's data, and it is shown on the verification in your dashboard. You can also supply one yourself on the create call as applicant.phone, in which case the step is what lets the applicant correct it. TrustWix does not confirm that a number reaches the person, so treat it as a detail they gave you rather than as a verified contact.

The check object

{
  "id": "chk_7d2e91af",
  "object": "check",
  "type": "document",
  "status": "completed",
  "verdict": "approve",
  "score": 0.93,
  "provider_tier": "standard",
  "reason_codes": [],
  "created_at": "2026-07-26T16:30:00.000Z",
  "updated_at": "2026-07-26T16:30:22.000Z"
}
FieldNotes
statuspending, processing, completed or failed. failed means the check could not be evaluated, which is not the same as a reject.
verdictapprove, reject, review, or null while the check is unresolved.
scoreHow strongly the check passed, 0 to 1. null when the check was skipped or produced no score.
provider_tierThe engine tier that ran this check: standard, pro or pro_max.
reason_codesStable upper snake case codes explaining a non-approval.

Verdict, score, and which to use

verdict is our decision about the check under your flow's policy, and it is what you branch on. It is a stable contract: three values, and the roll-up rule that turns them into one answer is published in Verdicts.

score is the signal that decision was made from. It is there so you can hold a passing check to your own bar as well as ours, which a three-valued verdict cannot express. "Approve, but route anything under 0.8 to my own team" is a real policy and this is the field that makes it writable. The same number is on every check in your dashboard, so the API is not showing you less than the screen does.

Use both, but do not swap their jobs. A verdict of reject is a rejection at every score, and moving your own threshold around cannot turn one into an approval.

What is deliberately not exposed

What we do not publish is anything that would name or characterise the engine behind a check: the models, the vendors, the sensitivity settings a score was compared against. Those change as the platform improves, they are meaningless outside the context they came from, and logic built on them would break silently. verdict, score and reason_codes are the contract.

Ordering

Checks run in the order the flow declares. Order matters where one check depends on another: a face match needs a document portrait to compare against, so a document step must come first. Your dashboard validates this for you when you build a flow.

On this page