Skip to main content

Voice biometrics: enrollment, challenge, verify

Voice biometrics verifies a person, not a device or a code. An OTP session proves a recipient can read a message sent to a number; a voiceprint proves the person speaking is the enrolled contact. The capability pairs with the verification session lifecycle as a second factor, or stands alone for agent-assisted call verification. Request and response shapes live in the Voice Biometrics API reference. This page explains the model behind it.

What a voiceprint is

A voiceprint is a stored speaker embedding — a 192-dimensional vector produced by an ECAPA-TDNN speaker model — bound to one of your contacts. Enrollment flow:
  1. You upload a short WAV clip (mono, up to 30 seconds) as base64 in POST /api/v1/verify/voice-biometrics/enroll, alongside the contact id and the consent text/version you captured. Biometric data requires explicit consent (GDPR Article 9); the consent string is stored with the record.
  2. The clip is analyzed before anything is stored. Two scores come back: the embedding and an anti-spoof score (bonafide probability from an AASIST model — near 1 means a live human, near 0 means a played-back or synthesized recording).
  3. If the clip is judged spoofed, enrollment is refused and nothing is written. If it passes, the embedding is encrypted at rest and stored against the contact, and you get back a vp_-prefixed voiceprint id.
Other properties you’ll run into:
  • Tenant enrollment cap. Each organization has a voiceprint quota; exceeding it returns 429 voiceprint_tenant_cap_exceeded.
  • Rate limits. Enrollment per contact is bounded per hour — enroll, don’t script a thousand retries.
  • Erasure is real. DELETE /api/v1/verify/voice-biometrics/{voiceprintId} permanently removes the voiceprint and its encrypted embedding (GDPR Article 17) — a hard delete, not a soft-delete flag. GET /api/v1/verify/voice-biometrics/enroll/{id} reports the enrollment status of a known id and returns 404 after erasure.
  • Metadata-only listing. GET /api/v1/verify/voice-biometrics/ lists voiceprints for your tenant — ids, contacts, timestamps — never embedding bytes.

The challenge and the verify step

Verification has two gates, and both must pass. The challenge (replay resistance). POST /api/v1/verify/voice-biometrics/challenge issues a per-attempt random phrase plus an opaque nonce id. Your caller reads the phrase aloud, records it, and your next POST /api/v1/verify/voice-biometrics/verify submits the audio together with the nonce id and phrase. The verify path consumes the nonce exactly once and rejects missing, expired, reused, or mismatched values — a recorded voice ripped from a previous call cannot satisfy a fresh phrase. Nonces expire after five minutes. The challenge is optional (older integrations keep working), but for anything fraud-sensitive you should use it. The verify gates. verify compares the submitted clip against the contact’s enrolled voiceprints:
  1. Cosine similarity. The new clip’s embedding is compared with the stored embeddings. This score must clear your confidence threshold.
  2. Anti-spoof. The clip must independently pass the anti-spoof check. A perfect voice match on a played-back recording still fails.
The verdict is therefore match AND live, never just match.

Tenant thresholds and the overview

Voice biometrics is tenant-scoped and tunable — the platform ships a default; the verdict is yours:
  • PATCH /api/v1/verify/voice-biometrics/settings sets your confidence_threshold (0.50 most permissive → 0.95 most strict) on the cosine-similarity gate, and the auto_2fa_on_low_confidence flag — when enabled, a below-threshold result signals step-up to a second factor instead of a hard failure.
  • GET /api/v1/verify/voice-biometrics/overview is the health view: enrolled voiceprint count, recent verification events, the 7-day false-accept-rate trend, and your current threshold settings. Watch the false-accept trend after loosening the threshold — that is the dial that tells you whether permissive became sloppy.
These controls are tenant-owned. There is no platform-wide “correct” threshold; a banking line and a loyalty hotline legitimately sit at different points on the range.

How the score composes with the risk surface

The composite fraud/risk verdict treats voice biometrics as an optional pass-through. When you run a voice check yourself — in an IVR, with an agent, in your own flow — you can feed the outcome into the pre-send risk score as the voice_biometrics_signal field, alongside verify_signal from Verify Fraud Guard. The composite takes the worst present channel score, so a failed biometric check cannot be washed out by an otherwise-clean aggregate. See the Risk API reference for the scoring bands.

The auth model

The public API endpoints above authenticate like every other Orbit API call (your API key). Behind them, the inference sidecar speaks a separate, internal-only protocol: HMAC-SHA256 request signatures on an X-Voice-Biometrics-Sig header (t=<unix_seconds>,v1=<hex>) with a ±60-second replay window. Raw audio never crosses an unsigned boundary, and the signature covers the request body — you cannot replay yesterday’s clip against today’s endpoint.

What this is not

  • Not the CAMARA network signals. SIM-swap and carrier-side signals come from the network operator; a voiceprint comes from the caller’s speech. Both can gate a flow, but they are different evidence types.
  • Not the composite risk score itself. Voice biometrics produces a verdict per verify call; the risk engine fuses that verdict with other signals only if you pass it through as voice_biometrics_signal.
  • Not an OTP code. There is nothing to intercept or forward — enrollment and verification need an actual human speaking. The challenge phrase exists precisely to keep it that way.