Behavioral Biometrics (BehavioIQ)

Compute a behavior score

POST/v1/behavior/score

Finalizes a challenge and returns the behavior score and risk level for the session, plus the recommended action when the policy check is enabled.

Request

MethodPOST
Path/v1/behavior/score
Content-Typeapplication/json
Auth requiredYes — Access Key

A server-side call that finalizes the challenge and triggers the scoring engine to analyze the collected behavioral data. Unlike the two SDK endpoints it takes plain JSON with Access Key authentication — no E2E encryption.

NameTypeRequiredDescription
challenge_idstringRequiredUUID of the challenge from create_challenge
transaction_dataobjectOptionalTransaction context for enhanced fraud scoring
is_updatebooleanOptionalOverrides whether this call updates the user's behavioral profile (baseline). Omit for the default behavior. See below.

transaction_data fields:

NameTypeRequiredDescription
transaction_idstringOptionalUnique transaction identifier
transaction_timestampstringOptionalISO 8601 timestamp
transaction_amountnumberOptionalTransaction amount
currency_codestringOptionalISO 4217 currency code (e.g. VND)
transaction_typestringOptionalType of transaction
channelstringOptionalTransaction channel
transaction_statusstringOptionalStatus of the transaction
account_numberstringOptionalSender account number (encrypted at rest)
account_holder_namestringOptionalSender account holder name (encrypted at rest)
bank_codestringOptionalSender bank code (encrypted at rest)
beneficiary_account_numberstringOptionalRecipient account number (encrypted at rest)
beneficiary_account_holder_namestringOptionalRecipient account holder name (encrypted at rest)
beneficiary_bank_codestringOptionalRecipient bank code (encrypted at rest)

Fields marked "encrypted at rest" are automatically encrypted by the server before storage. You should send them in plaintext.

transaction_data is also visible to behavior policies (see Policy decision below). For example, a policy can require a step-up challenge when transaction_amount exceeds a threshold — including on first-session (enrollment) calls.

The is_update parameter

By default, the user's behavioral profile (the enrolled baseline) is maintained automatically: the first session enrolls the baseline, and later sessions refresh it according to the policy configured for your deployment. The optional is_update flag lets your backend override this on a per-call basis.

is_updateEffect
omitted / nullDefault behavior. The first session enrolls the baseline; later sessions update the profile only when your deployment's policy opts in.
trueAlways update. This session is folded into the baseline regardless of policy. On a first session this enrolls the baseline as usual.
falseNever update. No change is made to the profile. On a first session (no baseline yet) no baseline is built — the response is still the synthetic first-session shape but with more_details.embedding_count: 0. On a later session the session is scored normally but read-only (the baseline is left untouched).

Once set, is_update takes priority over the default policy-driven behavior at every step. Use false for read-only scoring that must not mutate the profile, and true to force-enroll a known-good session. Omit the field to keep the default behavior — existing integrations need no change.

Read-only scoring (no profile update):

JSON
{
  "challenge_id": "d4e5f6a7-b8c9-0123-4567-89abcdef0123",
  "is_update": false
}

Sample request

JSON
{
  "challenge_id": "d4e5f6a7-b8c9-0123-4567-89abcdef0123",
  "transaction_data": {
    "transaction_id": "txn-001",
    "transaction_timestamp": "2024-01-15T10:30:00Z",
    "transaction_amount": 5000000,
    "currency_code": "VND",
    "transaction_type": "transfer",
    "channel": "mobile_app",
    "account_number": "1234567890",
    "beneficiary_account_number": "0987654321"
  }
}

Response

data.behavior_score carries the verdict and data.risk_level bands it. Scoring follows an enrollment / verification model, like fingerprint enrollment:

  • First session (enrollment). The first scored challenge for a (cus_user_id, client) pair builds the user's behavioral baseline. No real scoring takes place — the response is a synthetic safe default (behavior_score: 1000, risk_level: "LOW") carrying more_details.is_first_session: true as the unambiguous marker. Defer first-session risk decisions to your other layers (KYC, device, biometric).
  • Subsequent sessions (verification). Each later session is compared against the enrolled baseline. The score answers: "how similar is this session's behavior to the enrolled behavior?"
behavior_scorerisk_levelis_fraud
700 – 1000LOWfalse
400 – 699MEDIUMfalse
100 – 399HIGHfalse
0 – 99CRITICALtrue

Calling this endpoint again for an already-scored challenge re-scores it against the current baseline (the call is not idempotent); each call creates its own request_id.

FieldTypeDescription
data.request_idstringUUID of the scoring request (for tracking)
data.behavior_scoreintegerBehavior score (0 – 1000). Higher means more similar to the enrolled baseline (more trustworthy)
data.risk_levelstringLOW, MEDIUM, HIGH, or CRITICAL (see mapping table above)
data.is_fraudbooleantrue only when risk_level is CRITICAL. Omitted when false — treat absence as false
data.is_confidentbooleanScoring confidence flag. Omitted when false
data.groupsobjectPer-signal-group breakdown, keyed by group name (device_description, device_behavior, user_behavior), each {score, weight}
data.explainersarrayScoring explainers (may be empty)
data.more_detailsobjectPresent on first-session responses only: {"is_first_session": true, "embedding_count": N}
data.server_infosobjectServer processing metadata
decisionobjectFinal action verdict. Present only when the behavior policy check is enabled (see Policy decision)
evaluation_detailsobjectApplied-policy details. Present only when the behavior policy check is enabled
errorsarrayList of errors (only on failure; data.status is failure in that case)

Verification session:

JSON
{
  "data": {
    "request_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
    "behavior_score": 850,
    "risk_level": "LOW",
    "groups": {
      "device_description": { "score": 800, "weight": 0.2 },
      "device_behavior": { "score": 860, "weight": 0.4 },
      "user_behavior": { "score": 855, "weight": 0.4 }
    },
    "explainers": [],
    "server_infos": {}
  }
}

First session — enrollment:

JSON
{
  "data": {
    "request_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
    "behavior_score": 1000,
    "is_fraud": false,
    "risk_level": "LOW",
    "groups": {
      "device_description": { "score": 0, "weight": 0.2 },
      "device_behavior": { "score": 0, "weight": 0.4 },
      "user_behavior": { "score": 0, "weight": 0.4 }
    },
    "is_confident": false,
    "explainers": [],
    "more_details": {
      "is_first_session": true,
      "embedding_count": 1
    }
  }
}

Scoring failure — HTTP 200, data.status: "failure":

JSON
{
  "data": {
    "status": "failure",
    "request_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
    "server_infos": {}
  },
  "errors": [
    {
      "message": "<error description from the scoring engine>",
      "code": "<engine-defined error code>"
    }
  ]
}

Policy decision (decision + evaluation_details)

Deployments can enable a behavior-score policy check per client or access key (configured by TrustVision operators — contact your integration manager to enable it). When enabled, every response — including first-session responses — carries two additional top-level keys next to data:

JSON
{
  "data": { "...": "unchanged DS payload as above" },
  "decision": {
    "final_action": "STEP_UP",
    "action_payload": { "challenge_type": "FACE_AUTHEN" }
  },
  "evaluation_details": {
    "applied_policies": [
      {
        "policy_id": "bp_critical_face_authen",
        "policy_name": "CRITICAL risk → Face Authentication",
        "policy_version": "2026.06.04.1",
        "policy_outcome": "OVERRIDDEN"
      }
    ]
  }
}
FieldTypeDescription
decision.final_actionstringALLOW, STEP_UP, REVIEW, or BLOCK — the recommended action for this session
decision.action_payloadobjectAction parameters, or null. For STEP_UP: {"challenge_type": "SOFT_OTP" \| "FACE_AUTHEN"}
evaluation_details.applied_policiesarrayPolicies that matched this session (empty array when no policy matched)
applied_policies[].policy_idstringStable policy identifier
applied_policies[].policy_namestringHuman-readable policy name
applied_policies[].policy_versionstringPolicy version string
applied_policies[].policy_outcomestringHow the policy adjusted the default action: NO_CHANGE, ESCALATED, DOWNGRADED, or OVERRIDDEN

When no policy matches, final_action falls back to the default mapping from risk_level:

risk_levelDefault final_actionchallenge_type
LOWALLOW
MEDIUMSTEP_UPSOFT_OTP
HIGHSTEP_UPFACE_AUTHEN
CRITICALREVIEW

Policies are authored per deployment and can reference the full scoring context, including transaction_data (e.g. "require face authentication when a first-session transaction exceeds 1,000,000 VND").

Backward compatibility: when the policy check is not enabled (the default), neither decision nor evaluation_details is present and the response keeps the legacy data-only shape. Integrations should ignore unknown top-level keys.

Failure codes

Scoring failures (engine-side) return HTTP 200 with data.status: "failure" and engine-defined error codes. Request-validation errors (e.g. no_batch_events, challenge_not_found) return HTTP 4xx/503 with an errors-only body — {"errors": [{"code": "...", "message": "..."}]}, no data block.

The envelope is described under Error Responses, and the codes every Behavioral call can return are listed under Common Error Codes.