Verify a caller with KBA on a live call
The KBA caller verification concept page maps the model; this guide runs the flow as one task. By the end you can pick KBA (or deliberately not), start a challenge on a live call, submit the caller’s answers, open a verified session, and gate a sensitive action on it. The Verify console guide covers the OTP surfaces; this one covers the on-file-facts flow an agent or an IVR runs while the caller waits.1. When to use KBA vs OTP vs biometrics
Three identity checks solve three different moments. Pick per call, not per tenant.
KBA is the default first try for inbound calls: it needs nothing the caller doesn’t already have, and it draws on facts your CRM already holds. Fall back to an OTP send when the contact profile is thin, and to biometrics when the contact enrolled a voiceprint (biometrics is stronger evidence than knowledge factors and should win when available).
The full OTP decision tree lives in Verify profiles and fallback chains; the biometrics lifecycle in the voice biometrics walkthrough.
2. Stateful ledger or stateless token pair — pick one
Two endpoint families drive the same verdict:
Both end in the same gate: pass → a verified session with an expiry; fail → a bounded attempt budget burns down to lockout. Don’t mix the two surfaces on one call — the ledger’s attempt counter and the token pair’s per-contact budget are independent, and interleaving them makes lockout behaviour confusing to your team.
The
GET /voice/kba/:callId read computes the effective status at request time. A stored verified flag past its expiry reports as expired, never as verified — so a stale row from an old call can’t silently open a later gate.
3. Walk the stateful ledger end to end
Start the challenge
data.session_id— pass back onverify.data.questions— the 2–4 factor/prompt pairs the agent reads aloud.data.attempts/data.attempts_remaining— start at 0 and 3.
Submit the answers
data.results), the remaining attempt count, and a final status of verified, pending (more attempts allowed), or failed (locked).
Read state and handle the attempt budget
data.status: "pending" and a decremented data.attempts_remaining. Up to three attempts are allowed by default; the third mismatch flips the session to terminal failed, and start then answers 409 KBA_LOCKED. To open a fresh session after lockout, pass restart: true on start — nothing else routes around the lockout.
After verification, the session gates downstream actions for its TTL (four hours on the per-call ledger). Any sensitive endpoint that checks the gate — payment capture, PII disclosure, account changes — gets 403 KBA_VERIFICATION_REQUIRED until the session verifies, and again once it expires.
4. Walk the stateless token pair
Issue a challenge
contact_id or phone; the response carries a data.challenge_token, the factor list, and an expiry. A challenge is single-use and short-lived (five minutes) — issue it immediately before asking, while the caller is on the line.
Verify and receive the session token
data.session_token alongside data.verified: true and an assurance level. On any failure you get verified: false and a reason: the caller gave a wrong answer, the challenge expired, the challenge token was malformed, or the per-contact attempt budget tripped.
Gate a downstream action on session check
/session/check is the read-only gate downstream tools (an AI-agent tool call, a payment-capture flow, a PII lookup) call before proceeding. It never touches the database — it validates the token’s signature and expiry — so it’s cheap to call inline on every gated action.
5. Factor selection and fallbacks
A challenge draws from the facts on the linked contact record. Empty fields are simply skipped — a contact with no company on file never gets a company question. The defaults lean toward the most-common facts; the concept page lists the full per-surface factor set. Insufficient data. A contact with fewer than two verifiable fields can’t be meaningfully challenged: start answers422 KBA_INSUFFICIENT_PROFILE_DATA with the available count in the message. The token pair answers contact_not_found the same way when the lookup itself fails. Neither error is retryable with the same inputs — the fix is to switch verification method:
- Agent judgement: ask the caller for an account-level fact the agent can check in the CRM, then proceed with a manual note.
- OTP send: route into your Verify profile’s delivery chain and read a code back; see Verify profiles and fallback chains.
- Voice biometrics: if the contact enrolled a voiceprint on a prior call, verify by phrase instead; see the voice biometrics walkthrough.
6. Attempt budget, lockout, and concurrency
The attempt counter is the brute-force guard on an identity gate, so it has to be strict:- Ledger — three attempts by default; the session flips terminal
failedon the third mismatch and onlyrestart: trueopens a new one (409 KBA_LOCKEDuntil then). - Token pair — a per-contact sliding window (default five in fifteen minutes) shared by challenge issuance and verify attempts. Re-issuing challenges against the same caller doesn’t reset the budget — it spends against it.
7. What gates on the verified session, and how long it lasts
Sensitive actions check the gate before they proceed:- Payment capture — the IVR or agent tool reads a card only after KBA passes.
- PII disclosure — tools that read contact or account details gate first.
- Account changes — address updates, plan changes, SIM swaps all check.
verified_at, and the effective-status read computes at request time so a stored flag past its expiry reports as expired, never verified. On the token pair, the signed session token carries a 15-minute expiry inside its payload, and /session/check rejects it after that. Session expires mid-call → run the challenge again; the caller is still on the line, and restart stays cheap.
8. The audit ledger — what actually gets recorded
KBA never stores or logs an answer. Per submission, the platform records:- Which factor keys were checked (not the answer text).
- Pass/fail per factor, and the resulting session status.
- The session id, attempt count, verified/failed timestamps, and the operator id behind the action.
9. Worked example: gating a payment-capture action
An inbound caller asks to pay an invoice. The IVR (or the agent desktop) runs the challenge before it ever reads card digits:- Caller reaches the pay-by-phone menu. The IVR resolves the inbound call’s linked contact, then
POST /api/v1/voice/kba/{callId}/start— the response is three prompts. - The IVR (or agent) asks each prompt aloud. Answers go direct to
POST /api/v1/voice/kba/{callId}/verifywith the returnedsession_id. - On
verified, the payment-capture tool runs its own gate (GET /api/v1/voice/kba/{callId}→status: verified, inside TTL) and proceeds to DTMF card capture. On any mismatch the IVR re-reads the attempt counter from the verify response and coaches the caller through the remaining attempts; onfailedit falls back to an outbound callback after a manual review.
POST /api/v1/voice/kba/challenge, then submits the final answers through /verify, then calls the payment tool with the returned session_token. The tool calls /session/check before it reads card details. See AI agent voice handback for how a flow hands a verified session between the IVR, the AI agent, and back to a human.
Cross-links
- KBA caller verification concept — the model behind this walkthrough.
- Verify profiles and fallback chains — the OTP chain KBA sits ahead of.
- Voice biometrics enrollment walkthrough — the enrolled-voiceprint alternative when a contact has a voiceprint on file.
- AI agent voice handback — handing a verified session between an IVR and an AI agent.
- Verification session lifecycle — the OTP-code model opposite the KBA concept.