> ## Documentation Index
> Fetch the complete documentation index at: https://developers.mihu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Speaker verification

> Is this recording the voiceprint's person?

`POST /v1/voiceprints/verify` compares a recording with a voiceprint (stored `id` or `token`).

```bash theme={null}
curl https://stt.eu.mihu.ai/v1/voiceprints/verify -H "Authorization: Bearer $KEY" \
  -F voiceprint=9b2f6c1e-3d4a-4f8b-a1c2-7e5d9f0b3a61 -F file=@call-start.wav -F nonce=session-77
```

```json theme={null}
{"id": "e4c1…", "object": "voiceprint.verification", "voiceprint": "9b2f6c1e-…", "score": 0.81, "match": true,
 "decision": "match", "threshold": 0.55, "speech_seconds": 6.2, "created_at": 1793171000, "nonce": "session-77", "account": "7d1e…",
 "signed": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6Ij…"}
```

| Field | Meaning |
| - | - |
| `voiceprint` | Stored voiceprint `id` or a `store=false` `token` |
| `file` or `file_url` | The speech to check: at least 3 s of speech (else `422`), **5 s or more recommended**, at most 120 s, one voice |
| `nonce` | Optional (≤ 200 characters): your value (e.g. a session or challenge id), returned and signed — lets your backend refuse a replayed result |

* `score`: similarity of the two voices (about −1 to 1, higher = more alike); `match` = `score ≥ threshold` (0.55).
  `decision` gives three levels: `match` (≥ 0.60), `uncertain` (0.50–0.60: ask for more speech or another check),
  `no_match` (\< 0.50).
* Measured (Common Voice Turkish, 171 speakers, 20 s enrolment, 5 s test, threshold 0.55): wrongly accepted / wrongly
  rejected 0.3 % / 1.7 % on wideband audio, 0.8 % / 3.9 % on telephone audio, 0.7 % / 11 % on telephone audio with
  noise. With 3 s of speech the errors are about 2–3× higher. Real calls (other codecs, packet loss) can be worse.
* **A helper signal, not a login.** Verification is not protected against recorded or synthetic (cloned) voices. Use it
  together with other checks (caller number, knowledge questions, OTP), never as the only way into an account.
* Personal data: verifying a person by voice processes biometric data; your consent text must cover this purpose.
* Calls are not billed.

## Signed result: `signed`

`signed` is a JWT (JWS compact, `alg: EdDSA` — Ed25519) of the result, so a backend can trust a result that passed
through an app or another service. Claims: `iss` `"umay"`, `aud` (your `account`), `sub` (voiceprint id or `"token"`),
`jti` (verification id), `iat`, `exp` (5 minutes later), `score`, `match`, `decision`, `threshold`, `speech_seconds`, `nonce`. The
public key is a JWK set at `GET /v1/voiceprints/jwks` (cache it; `kid` names the key).

```python theme={null}
import jwt, requests                                  # PyJWT with the cryptography package
jwks = jwt.PyJWKClient("https://stt.eu.mihu.ai/v1/voiceprints/jwks", headers={"Authorization": f"Bearer {KEY}"})
key = jwks.get_signing_key_from_jwt(signed).key
claims = jwt.decode(signed, key, algorithms=["EdDSA"], audience=MY_ACCOUNT)   # checks signature, exp, aud
assert claims["nonce"] == expected_nonce and claims["match"] is True
```

```js theme={null}
import { createRemoteJWKSet, jwtVerify } from "jose";
const jwks = createRemoteJWKSet(new URL("https://stt.eu.mihu.ai/v1/voiceprints/jwks"), { headers: { Authorization: `Bearer ${KEY}` } });
const { payload } = await jwtVerify(signed, jwks, { algorithms: ["EdDSA"], audience: MY_ACCOUNT });
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.