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

# KBA caller-verification session errors: decode the seven codes and fix the session

> Decode every KBA error on the caller-verification flow — session-not-found and session-not-active on the per-call ledger, missing-contact lookup failures, insufficient profile data, the attempt-budget lockout, and the verification-required gate on sensitive actions.

# KBA caller-verification session errors

Knowledge-based authentication (KBA) challenges a live caller against facts
on file for the call's linked contact. Seven error codes fire across the
start → verify → gate flow, and they split into two families: **session and
lookup errors** (a session you named does not exist or can no longer accept
answers; the call or contact behind it resolved to nothing) and **gate
errors** (the verification itself refused to start or a guarded action has
no verified session). This page lets a supervisor or integrator work either
family end to end.

The concept behind the flow lives on the
[KBA caller verification concept page](/concepts/caller-verification-kba);
the happy-path walkthrough is the
[KBA caller-verification guide](/guides/kba-caller-verification). Read this
page when either of those surfaces returned an error instead of a session.

## 1. The session state machine

A KBA session on the per-call ledger moves through three states:

```text theme={null}
pending ──every answer matches──▶ verified
   │
   └──attempt budget exhausted──▶ failed
```

* **pending** — the start call issued the challenge; verify accepts answers
  and every mismatch burns one attempt.
* **verified** — every submitted factor matched. The session gates
  sensitive actions for four hours from `verified_at`; after that the
  status read computes as `expired`, never as verified, and gated actions
  refuse again until a fresh session verifies.
* **failed** — the attempt budget (three mismatches by default) ran out.
  Failed is terminal: only a deliberate `restart: true` on start opens a
  new challenge.

The ledger lives on the call record itself (`kba_verification` in the call
log's metadata), so one call carries one session at a time, and start is
idempotent while the session is pending — a repeated start re-reads the
same questions and counters rather than minting a new session.

## 2. The seven codes

Neither error family is retryable without changing the request. Decode the
code first.

| Code | HTTP | Fires on | Session state that fired it |
| - | - | - | - |
| `KBA_SESSION_NOT_FOUND` | 404 | `POST /voice/kba/:callId/verify` | No session on this call, or the `session_id` you submitted is not the one on the call record |
| `KBA_SESSION_NOT_ACTIVE` | 409 | `POST /voice/kba/:callId/verify` | The session on this call already reached `verified` or `failed` — it no longer accepts answers |
| `KBA_CONTACT_NOT_FOUND` | 422 | `POST /voice/kba/:callId/start` | The contact id linked to the call no longer resolves to a live contact row |
| `KBA_NO_CONTACT_LINKED` | 422 | `POST /voice/kba/:callId/start` | The call record has no linked contact, so there is no on-file identity to challenge against |
| `KBA_INSUFFICIENT_PROFILE_DATA` | 422 | `POST /voice/kba/:callId/start` | The linked contact has fewer than two verifiable fields on file (email, phone last 4, full name, company) |
| `KBA_LOCKED` | 409 | `POST /voice/kba/:callId/start` | The session on this call is terminal `failed` — the attempt budget is exhausted |
| `KBA_VERIFICATION_REQUIRED` | 403 | A guarded sensitive action | No verified, unexpired session on the call the action names |

The guard any sensitive endpoint (payment capture, PII disclosure, account
change) wires to refuse an unverified caller is what fires
`KBA_VERIFICATION_REQUIRED`; see [section 5](#5-the-verification-required-gate).

## 3. Session-not-found and session-not-active

`KBA_SESSION_NOT_FOUND` is almost always an integration ordering problem,
not a platform fault:

1. Your integration submitted answers to a call that never had a start, or
   post-surfaced a stale `session_id` from an earlier session on the same
   call. The response message names the fix: start a session first.
2. The session expired — a `verified` status persists as the ledger state,
   but four hours past `verified_at` the computed status is `expired`, and
   most downstream gates stop honoring it. Open a fresh session; the
   caller is usually still on the line. If the integration keeps a
   session across long calls, refresh it at TTL boundaries rather than
   caching `session_id` forever.
3. On the stateless token pair, the equivalent failure is a verify call
   with an expired or malformed `challenge_token` — the challenge is
   single-use and five-minute-lived, so submit answers while the caller is
   on the line, never against a token carried between calls.

`KBA_SESSION_NOT_ACTIVE` says the session is already settled — it reached
`verified` or `failed` — and verify rejects any further answers. Read the
session state (`GET /api/v1/voice/kba/:callId`) before submitting; a
`verified` ledger means the gate should be passing, a `failed` ledger is
permanent until restart. Never retry the same answers against a settled
session — nothing changes.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/voice/kba/{callId}" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

## 4. Locked, insufficient-profile, and contact-lookup failures

Start rejects with one of four codes before any challenge issues.

**`KBA_LOCKED` (409)** — the attempt budget ran out on a previous session,
so the ledger holds terminal `failed`. The only deliberate way past it is
`POST /voice/kba/:callId/start` with `restart: true` in the body:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/kba/{callId}/start" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restart": true }'
```

Anything that accidentally routes around the lockout (re-POSTing start
without restart, retrying verify) returns the same lock — the counter is
the brute-force guard, so it stays strict. On the stateless pair the
lockout is a per-contact sliding window (default five attempts in fifteen
minutes) and re-issuing `/challenge` spends against it — cycling the
challenge endpoint never resets the budget.

**`KBA_INSUFFICIENT_PROFILE_DATA` (422)** — the linked contact has fewer
than two verifiable facts on file. No retry of the same inputs passes:
challenge questions come from contact data, and thin data stays thin
across retries. Switch verification method:

* **Agent judgement** — agent reads an account-level fact the caller can
  confirm in the CRM, proceeds with a manual note on the call record.
* **OTP send** — run a verify profile's delivery chain (SMS/email) and
  read a code back; see
  [Verify OTP troubleshooting](/troubleshooting/verify-otp).
* **Voice biometrics** — if the contact enrolled a voiceprint, verify by
  phrase instead; see
  [voice biometrics failures](/troubleshooting/voice-biometrics-failures).

If thin profiles are routine for a segment, fill in the contact record
from your CRM sync — KBA only challenges on data you already hold.

**`KBA_NO_CONTACT_LINKED` and `KBA_CONTACT_NOT_FOUND` (both 422)** — the
call carries no `contact_id`, or the linked contact no longer resolves
(deleted or unlinked after the call started). Both are lookup failures at
start time: link a contact to the call through the voice/call stack or
the CRM integration first, then start. KBA\_NO\_CONTACT\_LINKED means "fix
your call-to-contact association"; KBA\_CONTACT\_NOT\_FOUND means "the
association points to a contact that is gone." The stateless pair
collapses the second one into its `contact_not_found` outcome on
`/challenge`.

## 5. The verification-required gate

Sensitive in-call actions refuse until a session verifies:

* **Payment capture** — card read (DTMF or agent entry) is held.
* **PII disclosure** — tools that read contact or account details gate
  first.
* **Account changes** — address updates, plan changes, SIM swaps, and
  similar mutations.

Any guarded endpoint returns `403 KBA_VERIFICATION_REQUIRED` with a
message that spells out the remediation (start → verify → retry). The
check is a read-only gate against the ledger's computed status — it never
mutates the session, so it is cheap to call inline. When the gate fires
mid-flow:

1. Read `GET /api/v1/voice/kba/:callId` — if the ledger shows
   `verified`+`expired`, the four-hour TTL slid past; re-run the
   challenge.
2. Run start → verify while the caller is still on the line.
3. Retry the original action once — never loop without the gate passing.

## 6. How the two surfaces differ

The stateful ledger and the stateless token pair carry the same verdict
but address it differently, and the error meaning folds over accordingly:

| Surface | Start | Session id | TTL | Lockout |
| - | - | - | - | - |
| Per-call ledger | `POST /voice/kba/:callId/start` | `session_id` returned on start | 4h from `verified_at` | `failed` state until `restart: true` |
| Stateless token pair | `POST /voice/kba/challenge` | `challenge_token` (5-min, single-use) | session\_token TTL (15 min) | per-contact sliding window (5 attempts / 15 min) |

Don't mix the two on one call — the ledger's per-session counter and the
token pair's per-contact window are independent, and interleaving them
makes lockout behavior impossible to explain to a reviewer.

## 7. Escalation bundle

A session/lookup error you can't clear with the steps above needs this in
a ticket:

* The `error.code` and the full JSON envelope.
* The `meta.request_id` off the failing response.
* The call id, the session id, and the start/verify timestamps off your
  integration log.
* The computed ledger state from `GET /api/v1/voice/kba/:callId`
  (attach it — it proves which of the three states you hit).

Never send support a caller's spoken answer text. KBA records factor keys
and pass/fail only — do the same in your own log scrub.

## Cross-links

* [KBA caller verification concept](/concepts/caller-verification-kba) —
  the model behind these states.
* [KBA caller-verification guide](/guides/kba-caller-verification) — the
  happy-path walkthrough this page triages against.
* [Verify OTP](/troubleshooting/verify-otp) — the fallback when the
  profile is too thin.
* [Voice biometrics failures](/troubleshooting/voice-biometrics-failures) —
  the enrolled-voiceprint fallback.
* [Error Code Reference](/reference/error-codes) — the seven codes'
  registry rows.
