# Get Invoice
Source: https://docs.voxmind.ai/api-reference/billing/invoices-get
GET /organisations/{orgId}/invoices/{invoiceId}
Retrieve a specific invoice by its ID, including usage quotas and document reference.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the invoice record.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/invoices/11 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 11,
"org_id": 42,
"sub_id": 3,
"invoice_sequence": 3,
"quotas": {
"monthly_verifications": 9847,
"overage_calls": 0
},
"file_ref": "4d61d50295d55da651aa534a049fc2f159414b89d8d29a56160a266539acfcfc",
"created_at": "2025-03-31T23:59:59Z",
"updated_at": "2025-04-01T00:00:01Z"
}
```
# List Invoices
Source: https://docs.voxmind.ai/api-reference/billing/invoices-list
GET /organisations/{orgId}/invoices
Retrieve all invoices for your organisation, including billing period usage and document references.
## Overview
Returns a paginated list of all invoices generated for your organisation. Each invoice corresponds to a billing period and includes a `file_ref` — a reference to the invoice PDF document. Use this endpoint to build an invoice history view in your own billing portal or to programmatically retrieve billing data for accounting systems.
Invoices are generated automatically at the end of each billing period and include your base subscription charge plus any overage calls billed at your tier's overage rate.
## Path Parameters
Your organisation's unique identifier.
## Query Parameters
Filter invoices. Example: `filter=sub_id=3` for all invoices under a specific subscription.
Example: `sort=created_at:desc` for most recent invoices first.
Results per page. Maximum 300.
Page number.
```bash cURL theme={null}
# Get the last 12 invoices
curl -X GET "https://api.voxmind.ai/organisations/42/invoices?sort=created_at:desc&per_page=12" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
resp = requests.get(
"https://api.voxmind.ai/organisations/42/invoices",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"sort": "created_at:desc", "per_page": 12},
)
invoices = resp.json()["results"]
for invoice in invoices:
print(f"Invoice #{invoice['invoice_sequence']} - Usage: {invoice['quotas']}")
```
```json 200 OK theme={null}
{
"results": [
{
"id": 11,
"org_id": 42,
"sub_id": 3,
"invoice_sequence": 3,
"quotas": {
"monthly_verifications": 9847,
"overage_calls": 0
},
"file_ref": "4d61d50295d55da651aa534a049fc2f159414b89d8d29a56160a266539acfcfc",
"created_at": "2025-03-31T23:59:59Z",
"updated_at": "2025-04-01T00:00:01Z"
}
]
}
```
# Get Plan
Source: https://docs.voxmind.ai/api-reference/billing/plans-get
GET /plans/{planId}
Retrieve details for a specific subscription plan by its ID.
## Path Parameters
The numeric ID of the plan. Retrieve plan IDs from the List Plans endpoint.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/plans/1 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 1,
"details": {
"name": "Starter",
"monthly_calls_included": 2500,
"overage_rate_gbp": 0.18
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
```
# List Plans
Source: https://docs.voxmind.ai/api-reference/billing/plans-list
GET /plans
Retrieve all available Voxmind subscription plans and their included quotas.
## Overview
Returns all subscription plans available on the Voxmind platform. Plan details include the included call quotas and configuration thresholds. Use this endpoint to dynamically display plan information in your own billing UI or to programmatically check what's included in a given plan before prompting a user to upgrade.
## Query Parameters
Filter plans by field values. Example: `filter=id=gte:2`
Sort order. Example: `sort=id:asc`
Results per page. Maximum 300.
Page number.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/plans \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
resp = requests.get(
"https://api.voxmind.ai/plans",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
plans = resp.json()["results"]
```
```json 200 OK theme={null}
{
"results": [
{
"id": 1,
"details": {
"name": "Starter",
"monthly_calls_included": 2500,
"overage_rate_gbp": 0.18
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
},
{
"id": 2,
"details": {
"name": "Growth",
"monthly_calls_included": 10000,
"overage_rate_gbp": 0.14
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
]
}
```
# Get Subscription
Source: https://docs.voxmind.ai/api-reference/billing/subscriptions-get
GET /organisations/{orgId}/subscriptions/{subscriptionId}
Retrieve a specific subscription record by its ID.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the subscription record.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/subscriptions/3 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 3,
"org_id": 42,
"plan_id": 2,
"start_date": "2025-03-01",
"end_date": "2025-03-31",
"created_at": "2025-03-01T00:00:00Z",
"updated_at": "2025-03-01T00:00:00Z"
}
```
# List Subscriptions
Source: https://docs.voxmind.ai/api-reference/billing/subscriptions-list
GET /organisations/{orgId}/subscriptions
Retrieve all subscriptions for your organisation, including current plan, billing period dates, and subscription status.
## Overview
Returns a list of subscription records for your organisation. In most cases, your organisation will have one active subscription at a time — the history of past subscriptions is accessible here if you need to audit plan changes or billing periods.
Subscriptions are managed via the Voxmind dashboard rather than the API — this endpoint is primarily for reading state, not for changing plans. To upgrade or downgrade your plan, use the self-service billing section at [developers.voxmind.ai/billing](https://developers.voxmind.ai/billing).
## Path Parameters
Your organisation's unique identifier.
## Query Parameters
Filter subscriptions. Example: `filter=plan_id=2` to find all subscriptions on the Growth plan.
Sort order. Example: `sort=start_date:desc` for most recent first.
Results per page. Maximum 300.
Page number.
```bash cURL theme={null}
# Get the current active subscription
curl -X GET "https://api.voxmind.ai/organisations/42/subscriptions?sort=start_date:desc&per_page=1" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
resp = requests.get(
"https://api.voxmind.ai/organisations/42/subscriptions",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"sort": "start_date:desc", "per_page": 1},
)
current_subscription = resp.json()["results"][0]
```
```json 200 OK theme={null}
{
"results": [
{
"id": 3,
"org_id": 42,
"plan_id": 2,
"start_date": "2025-03-01",
"end_date": "2025-03-31",
"created_at": "2025-03-01T00:00:00Z",
"updated_at": "2025-03-01T00:00:00Z"
}
]
}
```
# Get Device
Source: https://docs.voxmind.ai/api-reference/devices/get
GET /organisations/{orgId}/devices/{deviceId}
Retrieve a specific device record by its ID.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the device record. Retrieve device IDs from the List Devices endpoint.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/devices/501 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 501,
"user_id": 1001,
"fingerprint": "a3f9d2b1c8e74f5a6d0b2c3e1f9a8b7d...",
"type": 1,
"last_used": "2025-03-15T14:22:00Z",
"created_at": "2025-02-10T09:15:00Z",
"updated_at": "2025-03-15T14:22:00Z"
}
```
# List Devices
Source: https://docs.voxmind.ai/api-reference/devices/list
GET /organisations/{orgId}/devices
Retrieve all devices associated with enrolled users in your organisation.
## Overview
Returns a paginated list of device records for your organisation. A device record is created when a `device_fingerprint` is included in an enrollment or verification call. Devices are useful for analytics — understanding whether your users authenticate consistently from the same device, or whether device-switching patterns correlate with fraud.
Device records are created automatically when fingerprints are submitted. They cannot be created manually.
## Path Parameters
Your organisation's unique identifier.
## Query Parameters
Filter conditions. Example: `filter=type=1` for mobile devices only.
Example: `sort=last_used:desc`
Results per page. Maximum 300.
Page number.
```bash cURL theme={null}
curl -X GET "https://api.voxmind.ai/organisations/42/devices?sort=last_used:desc" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"results": [
{
"id": 501,
"user_id": 1001,
"fingerprint": "a3f9d2b1c8e74f5a6d0b2c3e1f9a8b7d...",
"type": 1,
"last_used": "2025-03-15T14:22:00Z",
"created_at": "2025-02-10T09:15:00Z",
"updated_at": "2025-03-15T14:22:00Z"
}
]
}
```
# Get Organisation
Source: https://docs.voxmind.ai/api-reference/organisation/get
GET /organisations/{orgId}
Retrieve your organisation's profile including contact details and tax identifiers.
## Overview
Returns the profile for your organisation — name, address, contact email, phone number, and tax identifiers (VAT and VIES numbers for EU compliance). This is read-only for most integrations, but useful when building an account management UI in your developer portal.
## Path Parameters
Your organisation's unique identifier.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
resp = requests.get(
"https://api.voxmind.ai/organisations/42",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
print(resp.json())
```
```json 200 OK theme={null}
{
"id": 42,
"name": "Acme Security Ltd.",
"address": "7 Bell Yard, London, England WC2A 2SJ, GB",
"email": "api@acmesecurity.com",
"phone_number": "+44 20 7946 0000",
"vat": "GB123456789",
"vies": "GB123456789",
"created_at": "2025-01-15T09:00:00Z",
"updated_at": "2025-02-01T14:22:00Z"
}
```
# Get Settings
Source: https://docs.voxmind.ai/api-reference/organisation/settings-get
GET /organisations/{orgId}/settings
Retrieve your organisation's configuration settings.
## Overview
Returns the configuration settings for your organisation. Settings control operational behaviour like alert preferences and notification thresholds. These are organisation-wide and affect all API calls made under your `orgId`.
## Path Parameters
Your organisation's unique identifier.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/settings \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 1,
"org_id": 42,
"settings": {
"alert_on_failure": true
},
"created_at": "2025-01-15T09:00:00Z",
"updated_at": "2025-02-01T14:22:00Z"
}
```
# Update Settings
Source: https://docs.voxmind.ai/api-reference/organisation/settings-update
PATCH /organisations/{orgId}/settings
Update your organisation's configuration settings.
## Path Parameters
Your organisation's unique identifier.
## Request Body
The associated organisation's ID. Must match the `orgId` path parameter.
A JSON object containing the settings fields to update. Currently supported settings:
* `alert_on_failure` (boolean) — When `true`, Voxmind sends a notification to your organisation's contact email when a verification failure rate exceeds normal thresholds. Useful for detecting coordinated attack patterns or integration issues in production.
```bash cURL theme={null}
curl -X PATCH https://api.voxmind.ai/organisations/42/settings \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"org_id": 42,
"settings": {
"alert_on_failure": true
}
}'
```
```python Python theme={null}
resp = requests.patch(
"https://api.voxmind.ai/organisations/42/settings",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"org_id": 42, "settings": {"alert_on_failure": True}},
)
```
```json 200 OK theme={null}
{
"id": 1,
"org_id": 42,
"settings": {
"alert_on_failure": true
},
"created_at": "2025-01-15T09:00:00Z",
"updated_at": "2025-03-15T11:30:00Z"
}
```
# Update Organisation
Source: https://docs.voxmind.ai/api-reference/organisation/update
PATCH /organisations/{orgId}
Update your organisation's profile details — name, address, contact info, and tax identifiers.
## Overview
Updates one or more fields in your organisation's profile. All fields are optional — only include the fields you want to change. Fields not included in the request body are left unchanged.
The primary use case is keeping billing contact details and tax identifiers current for invoice generation.
## Path Parameters
Your organisation's unique identifier.
## Request Body
The legal or trading name of your organisation as it should appear on invoices.
Full address including postcode and country, formatted as a single string.
Primary contact email for billing and account notifications.
Main phone number in E.164 format (e.g., `+44 20 7946 0000`).
VAT registration number. Required for EU/UK businesses to receive VAT-compliant invoices.
VIES number for EU intra-community trade. Same as VAT number for most organisations.
```bash cURL theme={null}
curl -X PATCH https://api.voxmind.ai/organisations/42 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Security Ltd.",
"email": "billing@acmesecurity.com",
"vat": "GB123456789"
}'
```
```python Python theme={null}
resp = requests.patch(
"https://api.voxmind.ai/organisations/42",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={
"name": "Acme Security Ltd.",
"email": "billing@acmesecurity.com",
"vat": "GB123456789",
},
)
```
```json 200 OK theme={null}
{
"id": 42,
"name": "Acme Security Ltd.",
"address": "7 Bell Yard, London, England WC2A 2SJ, GB",
"email": "billing@acmesecurity.com",
"phone_number": "+44 20 7946 0000",
"vat": "GB123456789",
"vies": "GB123456789",
"created_at": "2025-01-15T09:00:00Z",
"updated_at": "2025-03-15T11:00:00Z"
}
```
# Create Predefined Text
Source: https://docs.voxmind.ai/api-reference/predefined-texts/create
POST /organisations/{orgId}/predefined-texts
Create a custom voice prompt for a specific language and text code.
## Overview
Creates a new predefined text for your organisation. Each text is uniquely identified by the combination of `language` and `text_code` — you cannot have two texts with the same language and text\_code pair. If you want to update an existing text, use the Update Predefined Text endpoint instead.
## Path Parameters
Your organisation's unique identifier.
## Request Body
Your organisation's numeric ID. Must match the `orgId` path parameter.
The language of this text, in `[ISO 639-1]-[ISO 3166-1 alpha-2]` format. Example: `en-UK`, `fr-FR`. See [Language Support](/guides/language-support) for the full list.
An integer you define to categorise this text. Use a consistent convention across your organisation — for example, `1` = enrollment prompt, `2` = verification prompt, `3` = failure message. The values themselves are arbitrary; the meaning is defined by your implementation.
The actual text content — the prompt or instruction displayed or spoken to users. There is no hard character limit but keep prompts concise (under 200 characters) to avoid user friction.
```bash cURL theme={null}
# Create an English enrollment prompt
curl -X POST https://api.voxmind.ai/organisations/42/predefined-texts \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"org_id": 42,
"language": "en-UK",
"text_code": 1,
"text": "Please say your full name to register your voice."
}'
```
```python Python theme={null}
# Create prompts for multiple languages
prompts = [
{"language": "en-UK", "text_code": 1, "text": "Please say your full name to register your voice."},
{"language": "fr-FR", "text_code": 1, "text": "Veuillez dire votre nom complet pour enregistrer votre voix."},
{"language": "de-DE", "text_code": 1, "text": "Bitte sagen Sie Ihren vollständigen Namen, um Ihre Stimme zu registrieren."},
]
for prompt in prompts:
resp = requests.post(
"https://api.voxmind.ai/organisations/42/predefined-texts",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"org_id": 42, **prompt},
)
print(resp.status_code, resp.json())
```
```json 201 Created theme={null}
{
"id": 23,
"org_id": 42,
"language": "en-UK",
"text_code": 1,
"text": "Please say your full name to register your voice.",
"created_at": "2025-03-15T10:00:00Z",
"updated_at": "2025-03-15T10:00:00Z"
}
```
```json 409 Conflict theme={null}
{
"code": 409,
"message": "A predefined text with language 'en-UK' and text_code 1 already exists for this organisation"
}
```
# Delete Predefined Text
Source: https://docs.voxmind.ai/api-reference/predefined-texts/delete
DELETE /organisations/{orgId}/predefined-texts/{textId}
Permanently delete a predefined text record.
## Overview
Permanently removes a predefined text from your organisation. This does not affect any historical enrollments or verifications — predefined texts are UX configuration only and are not embedded in voiceprint data.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the predefined text to delete.
```bash cURL theme={null}
curl -X DELETE https://api.voxmind.ai/organisations/42/predefined-texts/23 \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
```python Python theme={null}
resp = requests.delete(
"https://api.voxmind.ai/organisations/42/predefined-texts/23",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
# 200 = deleted
```
```json 200 OK theme={null}
{
"id": 23,
"org_id": 42,
"language": "en-UK",
"text_code": 1,
"text": "Please clearly say your full name to complete voice registration.",
"created_at": "2025-03-15T10:00:00Z",
"updated_at": "2025-03-15T16:45:00Z"
}
```
# Get Predefined Text
Source: https://docs.voxmind.ai/api-reference/predefined-texts/get
GET /organisations/{orgId}/predefined-texts/{textId}
Retrieve a specific predefined text by its ID.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the predefined text record.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/predefined-texts/23 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 23,
"org_id": 42,
"language": "en-UK",
"text_code": 1,
"text": "Please say your full name to register your voice.",
"created_at": "2025-03-15T10:00:00Z",
"updated_at": "2025-03-15T10:00:00Z"
}
```
# List Predefined Texts
Source: https://docs.voxmind.ai/api-reference/predefined-texts/list
GET /organisations/{orgId}/predefined-texts
Retrieve all custom text prompts configured for your organisation, used to guide users during enrollment and verification.
## Overview
Predefined texts are the voice prompts or on-screen instructions you configure to guide users through enrollment and verification. Because Voxmind is text-independent, users can say anything — but in a well-designed authentication UX, you'll want to present them with a specific prompt that feels natural and consistent with your application.
For example, a contact centre might configure: *"Please say your name and account reference to verify your identity."* A banking app might use: *"Please say your passphrase to confirm this transaction."* Each text is associated with a language code and a `text_code` integer that you define to categorise it (e.g. `1` = enrollment prompt, `2` = verification prompt).
Predefined texts are entirely optional configuration. If you don't configure any, Voxmind still processes voice data correctly — you'd simply handle the user-facing prompt within your own application UI.
## Path Parameters
Your organisation's unique identifier.
## Query Parameters
Filter texts. Example: `filter=language=en-UK` for all English texts, or `filter=text_code=1` for all enrollment prompts.
Sort order. Example: `sort=language:asc`
Results per page. Maximum 300.
Page number.
```bash cURL theme={null}
# Get all English texts
curl -X GET "https://api.voxmind.ai/organisations/42/predefined-texts?filter=language=en-UK" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
resp = requests.get(
"https://api.voxmind.ai/organisations/42/predefined-texts",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"filter": "language=en-UK"},
)
texts = resp.json()["results"]
```
```json 200 OK theme={null}
{
"results": [
{
"id": 21,
"org_id": 42,
"language": "en-UK",
"text_code": 1,
"text": "Please say your full name to register your voice.",
"created_at": "2025-02-01T09:00:00Z",
"updated_at": "2025-02-01T09:00:00Z"
},
{
"id": 22,
"org_id": 42,
"language": "en-UK",
"text_code": 2,
"text": "Please say your name and account number to verify your identity.",
"created_at": "2025-02-01T09:00:00Z",
"updated_at": "2025-02-01T09:00:00Z"
}
]
}
```
# Update Predefined Text
Source: https://docs.voxmind.ai/api-reference/predefined-texts/update
PATCH /organisations/{orgId}/predefined-texts/{textId}
Update the content of an existing predefined text. Use this to refine prompt wording without creating a new record.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the predefined text to update.
## Request Body
Your organisation's numeric ID.
Updated language code. Note: changing the language while keeping the same `text_code` is valid as long as the new language + text\_code combination doesn't already exist.
Updated category code.
The updated text content.
```bash cURL theme={null}
curl -X PATCH https://api.voxmind.ai/organisations/42/predefined-texts/23 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Please clearly say your full name to complete voice registration."
}'
```
```python Python theme={null}
resp = requests.patch(
"https://api.voxmind.ai/organisations/42/predefined-texts/23",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"text": "Please clearly say your full name to complete voice registration."},
)
```
```json 200 OK theme={null}
{
"id": 23,
"org_id": 42,
"language": "en-UK",
"text_code": 1,
"text": "Please clearly say your full name to complete voice registration.",
"created_at": "2025-03-15T10:00:00Z",
"updated_at": "2025-03-15T16:45:00Z"
}
```
# Issue API Token
Source: https://docs.voxmind.ai/api-reference/tokens/create
POST /organisations/{orgId}/api-tokens
Issue a new long-lived API token for your organisation. The bearer value is shown only once — store it immediately.
## Overview
Creates a new API token for your organisation. The `bearer` field in the response is the raw token string you'll use in `Authorization: Bearer` headers — this is the **only time it will ever be returned**. Voxmind stores only a hashed version and cannot retrieve it for you. Copy it to your secrets manager immediately.
**Save the bearer token immediately.** After the creation response, the raw token value is permanently inaccessible. If you lose it, you'll need to issue a new token and revoke the lost one.
## Path Parameters
Your organisation's unique identifier.
## Request Body
A human-readable label to identify this token. Use descriptive names that reflect where the token is used — `production-api-server`, `staging-integration`, `avaya-webhook-handler`. Makes auditing and rotation significantly easier.
Optional expiry time in UTC ISO 8601 format. If omitted, the token never expires. Voxmind recommends setting an expiry and implementing a rotation schedule for production tokens.
Optional. The earliest time from which the token is valid. Useful when pre-generating a replacement token ahead of a planned rotation.
Whether the token is active at creation. Typically `true` — use `false` only if you're pre-generating a token for future activation.
```bash cURL theme={null}
# Issue a production token with a 90-day expiry
curl -X POST https://api.voxmind.ai/organisations/42/api-tokens \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"friendly_name": "production-server-01",
"expires_at": "2025-06-01T00:00:00Z",
"enabled": true
}'
```
```python Python theme={null}
import requests
resp = requests.post(
"https://api.voxmind.ai/organisations/42/api-tokens",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={
"friendly_name": "production-server-01",
"expires_at": "2025-06-01T00:00:00Z",
"enabled": True,
},
)
data = resp.json()
# Store data["bearer"] in your secrets manager NOW
```
```javascript Node.js theme={null}
const response = await fetch(
"https://api.voxmind.ai/organisations/42/api-tokens",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
friendly_name: "production-server-01",
expires_at: "2025-06-01T00:00:00Z",
enabled: true,
}),
}
);
const { bearer, token } = await response.json();
// Store `bearer` in AWS Secrets Manager or equivalent — never log it
```
```json 201 Created theme={null}
{
"bearer": "eyJjbGllbnRfaWQiOiJZekV6TUdkb01ISm5PSEJpT0cxaWJE...",
"token": {
"id": 8,
"subject": "org_42",
"audience": "PUBLIC_API",
"algorithm": "HS256",
"friendly_name": "production-server-01",
"expires_at": "2025-06-01T00:00:00Z",
"not_before": null,
"enabled": true,
"created_at": "2025-03-01T09:00:00Z",
"updated_at": "2025-03-01T09:00:00Z"
}
}
```
```json 409 Conflict theme={null}
{
"code": 409,
"message": "A token with this friendly_name already exists for your organisation"
}
```
# Delete Token
Source: https://docs.voxmind.ai/api-reference/tokens/delete
DELETE /organisations/{orgId}/api-tokens/{tokenId}
Permanently delete an API token record. The token is immediately invalidated and cannot be recovered.
## Overview
Permanently removes a token from your organisation. Any API request using this token after deletion will return `401 Unauthorized`. Unlike disabling a token (PATCH), deletion is irreversible — use it for tokens that are confirmed decommissioned and no longer needed for audit trail purposes.
Deleting a token that is still in use by a production service will immediately break those API calls. Confirm the token is no longer in use before deleting. When in doubt, disable first (PATCH `enabled: false`) and monitor for 401 errors before proceeding to delete.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the token to permanently delete.
```bash cURL theme={null}
curl -X DELETE https://api.voxmind.ai/organisations/42/api-tokens/8 \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
```python Python theme={null}
resp = requests.delete(
"https://api.voxmind.ai/organisations/42/api-tokens/8",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
# 200 = deleted successfully
```
```json 200 OK theme={null}
{
"id": 8,
"friendly_name": "production-server-01",
"enabled": false,
"created_at": "2025-03-01T09:00:00Z",
"updated_at": "2025-03-15T12:00:00Z"
}
```
# Get Token
Source: https://docs.voxmind.ai/api-reference/tokens/get
GET /organisations/{orgId}/api-tokens/{tokenId}
Retrieve metadata for a specific API token by its ID.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the token. Retrieve this from the List API Tokens response.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/api-tokens/8 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```json 200 OK theme={null}
{
"id": 8,
"subject": "org_42",
"audience": "PUBLIC_API",
"algorithm": "HS256",
"friendly_name": "production-server-01",
"expires_at": "2025-06-01T00:00:00Z",
"not_before": null,
"enabled": true,
"created_at": "2025-03-01T09:00:00Z",
"updated_at": "2025-03-01T09:00:00Z"
}
```
# List API Tokens
Source: https://docs.voxmind.ai/api-reference/tokens/list
GET /organisations/{orgId}/api-tokens
Retrieve all API tokens for your organisation. Supports filtering, sorting, and pagination.
## Overview
Returns a paginated list of all API tokens associated with your organisation. Use this to audit active tokens, find tokens that haven't been used recently, and identify tokens that should be rotated or revoked.
Token values are never returned in list or get responses — only metadata. The raw bearer token string is only ever shown once, at creation time.
## Path Parameters
Your organisation's unique identifier.
## Query Parameters
Comma-separated filter conditions. Supports `=`, `!=`, and comparators `gt`, `gte`, `lt`, `lte`, `like`, `ilike`.
Example: `filter=enabled=true,friendly_name=like:production%`
Comma-separated sort fields with direction. Example: `sort=created_at:desc,friendly_name:asc`
Number of results per page. Maximum 300.
Page number. Check the `X-Total-Pages` response header for the total number of pages.
```bash cURL theme={null}
# List all enabled tokens, newest first
curl -X GET "https://api.voxmind.ai/organisations/42/api-tokens?filter=enabled=true&sort=created_at:desc" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
import requests
resp = requests.get(
"https://api.voxmind.ai/organisations/42/api-tokens",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"filter": "enabled=true", "sort": "created_at:desc", "per_page": 50}
)
print(resp.json())
```
```javascript Node.js theme={null}
const response = await fetch(
"https://api.voxmind.ai/organisations/42/api-tokens?filter=enabled=true&sort=created_at:desc",
{ headers: { Authorization: "Bearer YOUR_API_TOKEN" } }
);
const tokens = await response.json();
```
```json 200 OK theme={null}
{
"results": [
{
"id": 7,
"subject": "org_42",
"audience": "PUBLIC_API",
"algorithm": "HS256",
"friendly_name": "production-server-01",
"expires_at": null,
"not_before": "2025-03-01T00:00:00Z",
"enabled": true,
"created_at": "2025-03-01T09:00:00Z",
"updated_at": "2025-03-01T09:00:00Z"
}
]
}
```
# Update Token
Source: https://docs.voxmind.ai/api-reference/tokens/update
PATCH /organisations/{orgId}/api-tokens/{tokenId}
Enable or disable an API token. The primary use case is disabling a token you suspect may be compromised without permanently deleting it.
## Overview
The update endpoint currently supports toggling the `enabled` state of a token. Disabling a token immediately prevents it from being used for API calls while preserving its record for audit purposes. This is preferable to deletion when you want to retain a history of all tokens ever issued.
A disabled token will return `401 Unauthorized` on any API request that uses it.
## Path Parameters
Your organisation's unique identifier.
The numeric ID of the token to update.
## Request Body
Set to `false` to immediately invalidate the token. Set back to `true` to re-enable it.
```bash cURL theme={null}
# Disable a token immediately
curl -X PATCH https://api.voxmind.ai/organisations/42/api-tokens/8 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
```
```python Python theme={null}
resp = requests.patch(
"https://api.voxmind.ai/organisations/42/api-tokens/8",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"enabled": False},
)
```
```json 200 OK theme={null}
{
"id": 8,
"friendly_name": "production-server-01",
"enabled": false,
"updated_at": "2025-03-15T10:30:00Z"
}
```
# Delete User
Source: https://docs.voxmind.ai/api-reference/users/delete
DELETE /organisations/{orgId}/users/{userId}
Permanently delete a user and their associated voiceprint data. Irreversible. Use for GDPR right-to-erasure requests.
## Overview
Permanently removes a user record and all associated voiceprint data from Voxmind's systems. This action cannot be undone. After deletion, the `external_id` is free to be re-enrolled as a new user if needed.
Deletion is permanent and includes the voiceprint. If you only want to prevent a user from authenticating temporarily, use the Update User endpoint to set `is_active: false` instead. Reserve deletion for permanent removal scenarios.
The primary use case for deletion is fulfilling GDPR right-to-erasure requests — when a user requests that your platform delete all data associated with them, this endpoint ensures Voxmind's copy of their biometric data is also removed.
## Path Parameters
Your organisation's unique identifier.
Voxmind's internal numeric ID for the user to permanently delete.
```bash cURL theme={null}
curl -X DELETE https://api.voxmind.ai/organisations/42/users/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
```python Python theme={null}
# Typical GDPR erasure workflow:
# 1. Find the Voxmind user ID from your external_id
resp = requests.get(
"https://api.voxmind.ai/organisations/42/users",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"filter": "external_id=user_98765"},
)
voxmind_id = resp.json()["results"][0]["id"]
# 2. Delete the record (voiceprint included)
resp = requests.delete(
f"https://api.voxmind.ai/organisations/42/users/{voxmind_id}",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
# 200 = permanently deleted
```
```json 200 OK theme={null}
{
"id": 1001,
"org_id": 42,
"external_id": "user_98765",
"is_active": false,
"created_at": "2025-02-10T09:15:00Z",
"updated_at": "2025-03-15T14:00:00Z"
}
```
# Get User
Source: https://docs.voxmind.ai/api-reference/users/get
GET /organisations/{orgId}/users/{userId}
Retrieve a single user record by their Voxmind internal ID.
## Path Parameters
Your organisation's unique identifier.
Voxmind's internal numeric ID for the user. Note: this is the Voxmind-assigned `id`, not your `external_id`. If you only have the `external_id`, use the List Users endpoint with `filter=external_id=YOUR_ID` to find the corresponding Voxmind user record.
```bash cURL theme={null}
curl -X GET https://api.voxmind.ai/organisations/42/users/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
# If you have the external_id and need the Voxmind user ID first:
resp = requests.get(
"https://api.voxmind.ai/organisations/42/users",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"filter": "external_id=user_98765"},
)
voxmind_user_id = resp.json()["results"][0]["id"]
# Then fetch the full user record
resp = requests.get(
f"https://api.voxmind.ai/organisations/42/users/{voxmind_user_id}",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
```
```json 200 OK theme={null}
{
"id": 1001,
"org_id": 42,
"external_id": "user_98765",
"is_active": true,
"created_at": "2025-02-10T09:15:00Z",
"updated_at": "2025-02-10T09:15:00Z"
}
```
# List Users
Source: https://docs.voxmind.ai/api-reference/users/list
GET /organisations/{orgId}/users
Retrieve all users registered under your organisation. Supports filtering by active status, sorting, and pagination.
## Overview
Returns a paginated list of users in your organisation. In the Voxmind data model, a "user" is any person who has been enrolled — they have a record in Voxmind's system tied to your `external_id` mapping. This endpoint is primarily useful for auditing your enrolled user base, checking active status, and integrating with your own user management dashboard.
Voxmind users are identified by your `external_id`, not by any email or identity field. Voxmind does not store personal data about your users — just the `external_id` and voiceprint. Your application is responsible for mapping `external_id` to real users.
## Path Parameters
Your organisation's unique identifier.
## Query Parameters
Filter conditions. Useful patterns: `filter=is_active=true` (enrolled active users only), `filter=external_id=like:user_%` (users with a specific ID prefix).
Example: `sort=created_at:desc`
Results per page. Maximum 300.
Page number. Total pages available in `X-Total-Pages` response header.
```bash cURL theme={null}
curl -X GET "https://api.voxmind.ai/organisations/42/users?filter=is_active=true&sort=created_at:desc&per_page=50" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
```python Python theme={null}
resp = requests.get(
"https://api.voxmind.ai/organisations/42/users",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"filter": "is_active=true", "sort": "created_at:desc", "per_page": 50},
)
users = resp.json()["results"]
total_pages = int(resp.headers.get("X-Total-Pages", 1))
```
```json 200 OK theme={null}
{
"results": [
{
"id": 1001,
"org_id": 42,
"external_id": "user_98765",
"is_active": true,
"created_at": "2025-02-10T09:15:00Z",
"updated_at": "2025-02-10T09:15:00Z"
},
{
"id": 1002,
"org_id": 42,
"external_id": "user_11223",
"is_active": true,
"created_at": "2025-02-11T14:30:00Z",
"updated_at": "2025-02-11T14:30:00Z"
}
]
}
```
# Update User
Source: https://docs.voxmind.ai/api-reference/users/update
PATCH /organisations/{orgId}/users/{userId}
Update a user's active status. Use this to deactivate users without deleting their voiceprint record.
## Overview
Currently supports updating the `is_active` flag on a user record. Setting `is_active: false` deactivates a user — they will no longer be able to authenticate via Voxmind until reactivated. The voiceprint data is preserved, so reactivation is instant with no re-enrollment required.
This is the preferred approach when a user leaves your platform, has their account suspended, or should temporarily lose voice authentication access. Use deletion only when you need to permanently remove voiceprint data (e.g. GDPR erasure requests).
## Path Parameters
Your organisation's unique identifier.
Voxmind's internal numeric ID for the user.
## Request Body
Set to `false` to deactivate the user. Set back to `true` to reactivate.
```bash cURL theme={null}
# Deactivate a user (e.g. account suspension)
curl -X PATCH https://api.voxmind.ai/organisations/42/users/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_active": false}'
```
```python Python theme={null}
# Deactivate
requests.patch(
"https://api.voxmind.ai/organisations/42/users/1001",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"is_active": False},
)
# Reactivate later
requests.patch(
"https://api.voxmind.ai/organisations/42/users/1001",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"is_active": True},
)
```
```json 200 OK theme={null}
{
"id": 1001,
"org_id": 42,
"external_id": "user_98765",
"is_active": false,
"created_at": "2025-02-10T09:15:00Z",
"updated_at": "2025-03-15T12:00:00Z"
}
```
# Enroll a User
Source: https://docs.voxmind.ai/api-reference/voice/enroll
POST /organisations/{orgId}/enrollments
Create a voiceprint for a user from a voice recording. Returns 202 immediately; result delivered via webhook.
## Overview
Enrollment is the process of creating a voiceprint — a mathematical representation of a user's unique vocal characteristics — from a voice recording. Once enrolled, a user can be verified at any time by submitting a new recording and comparing it against their stored voiceprint.
Enrollment is **asynchronous**. You submit the request, receive a 202 Accepted immediately, and Voxmind delivers the result (including voiceprint quality score and status) to your configured callback URL when processing is complete. This typically takes 1–3 seconds.
A user's `external_id` is your identifier — use whatever format you use in your own system (UUID, integer, email, etc.). Voxmind doesn't validate the format; it just stores and returns it. Consistency is what matters: the same `external_id` must be used in all future verification calls for this user.
## Path Parameters
Your organisation's unique identifier. Found in your dashboard or returned by `GET /organisations/{orgId}`.
## Request Body
The user's voice recording, base64-encoded. Accepted formats: WAV, MP3. Minimum 3 seconds of speech; 5 seconds recommended for optimal accuracy. Minimum sample rate: 16kHz.
A unique identifier you generate for this request. Voxmind returns this value in the webhook callback so you can match the async result to the originating request. Use UUID v4.
Your user's identifier in your system. This is the key used to associate verifications with this enrollment. Must be consistent across all calls for the same user.
The primary language of the audio, in `[ISO 639-1]-[ISO 3166-1 alpha-2]` format (e.g., `en-UK`, `fr-FR`, `de-DE`, `es-ES`). Voxmind is language-agnostic — users can verify in any language after enrolling — but specifying the language improves phoneme boundary detection accuracy. See [Language Support](/guides/language-support) for all supported values.
Optional. A unique identifier for the device the user is using during enrollment. Used for device-level fraud analytics and to associate voiceprints with specific hardware.
Optional. An integer representing the category of device. `0` = unknown, `1` = mobile phone, `2` = desktop/laptop, `3` = IP phone/desk phone. Used for analytics and to contextualise match scores — audio characteristics vary across device types.
## Response
The webhook URL Voxmind will call with the enrollment result. This is your configured callback URL pulled from organisation settings.
A human-readable confirmation that the request was accepted and is being processed.
```bash cURL theme={null}
curl -X POST https://api.voxmind.ai/organisations/42/enrollments \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"voice_data": "UklGRiQAAABXQVZFZm10IBAAAA...",
"request_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "user_98765",
"language": "en-UK",
"device_type": 1
}'
```
```python Python theme={null}
import requests, base64, uuid
with open("user_voice.wav", "rb") as f:
voice_b64 = base64.b64encode(f.read()).decode("utf-8")
resp = requests.post(
"https://api.voxmind.ai/organisations/42/enrollments",
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
json={
"voice_data": voice_b64,
"request_uuid": str(uuid.uuid4()),
"external_id": "user_98765",
"language": "en-UK",
}
)
print(resp.status_code, resp.json())
```
```javascript Node.js theme={null}
const fs = require("fs");
const { randomUUID } = require("crypto");
const voiceData = fs.readFileSync("user_voice.wav").toString("base64");
const response = await fetch(
"https://api.voxmind.ai/organisations/42/enrollments",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
voice_data: voiceData,
request_uuid: randomUUID(),
external_id: "user_98765",
language: "en-UK",
}),
}
);
console.log(await response.json());
```
```json 202 Accepted theme={null}
{
"callback_url": "https://yourapp.com/webhooks/voxmind",
"message": "Your request has been accepted and is being processed"
}
```
```json 400 Bad Request theme={null}
{
"code": 400,
"message": "Validation failed",
"errors": [
{
"field": "voice_data",
"message": "This field is required"
},
{
"field": "request_uuid",
"message": "Must be a valid UUID v4"
}
]
}
```
```json 401 Unauthorized theme={null}
{
"code": 401,
"message": "Invalid or expired authentication credentials"
}
```
# Verify a User
Source: https://docs.voxmind.ai/api-reference/voice/verify
POST /organisations/{orgId}/verifications
Verify a user's identity against their enrolled voiceprint, with automatic deepfake detection. Returns 202 immediately; result delivered via webhook.
## Overview
Verification compares a new voice recording against a user's stored voiceprint. Voxmind runs two parallel checks: a voiceprint match (returning a confidence score) and a deepfake detection check (identifying synthetic or replayed audio). Both results are included in the webhook payload.
Like enrollment, verification is **asynchronous**. You receive a 202 Accepted immediately and your webhook receives the full result within 1–2 seconds on average.
**Always check `deepfake_detected` before granting access.** A voice clone may produce a non-trivial match score. The deepfake flag is your definitive signal — any result with `deepfake_detected: true` should be treated as a security event and logged, regardless of the `match_score`.
## Path Parameters
Your organisation's unique identifier.
## Request Body
The voice recording from the current authentication attempt, base64-encoded. Same format requirements as enrollment: WAV or MP3, minimum 16kHz, at least 3 seconds of speech.
A unique identifier you generate for this verification request. Returned in the webhook payload so you can correlate the async result with the correct user session.
Your user's identifier — must exactly match the `external_id` used during their enrollment. Voxmind uses this to retrieve the correct voiceprint for comparison.
The primary language of the verification audio. The user can speak a different language than they enrolled in — Voxmind handles this — but specifying the correct language improves accuracy.
Optional. The unique identifier of the device being used for this verification attempt. When provided, this is compared against the device fingerprint used at enrollment. Mismatches are flagged in analytics and can be used to detect account sharing or device-switching attacks.
Optional. Same device type categories as enrollment. `0` = unknown, `1` = mobile, `2` = desktop, `3` = IP phone.
## Webhook Result Payload
The following fields are included in the webhook result delivered to your callback URL:
Always `verification.completed` for verification results.
The UUID you provided in the request body. Use this to correlate the result with the correct user session.
Your user's identifier, as provided in the request.
The verification outcome. One of `verified`, `rejected`, or `inconclusive`. An `inconclusive` result means audio quality was insufficient for a reliable determination — treat it as a rejection and prompt the user to try again.
A confidence score between 0.0 and 1.0 representing how closely the submitted audio matches the enrolled voiceprint. Scores above 0.85 indicate a strong match. Your application should define a threshold appropriate to your security requirements — higher stakes use cases should use a higher threshold.
`true` if the audio was identified as AI-generated, synthetic, or replayed. This runs as a parallel check to voiceprint matching and is always present in the payload. A value of `true` should result in immediate rejection and fraud logging, regardless of `match_score`.
Total processing time in milliseconds from request receipt to result generation. Useful for monitoring and SLA verification.
```bash cURL theme={null}
curl -X POST https://api.voxmind.ai/organisations/42/verifications \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"voice_data": "UklGRiQAAABXQVZFZm10IBAAAA...",
"request_uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"external_id": "user_98765",
"language": "en-UK"
}'
```
```python Python theme={null}
import requests, base64, uuid
with open("auth_attempt.wav", "rb") as f:
voice_b64 = base64.b64encode(f.read()).decode("utf-8")
resp = requests.post(
"https://api.voxmind.ai/organisations/42/verifications",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={
"voice_data": voice_b64,
"request_uuid": str(uuid.uuid4()),
"external_id": "user_98765", # Must match enrollment external_id
"language": "en-UK",
}
)
```
```javascript Node.js theme={null}
const fs = require("fs");
const { randomUUID } = require("crypto");
const response = await fetch(
"https://api.voxmind.ai/organisations/42/verifications",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
voice_data: fs.readFileSync("auth_attempt.wav").toString("base64"),
request_uuid: randomUUID(),
external_id: "user_98765",
language: "en-UK",
}),
}
);
```
```json 202 Accepted (immediate response) theme={null}
{
"callback_url": "https://yourapp.com/webhooks/voxmind",
"message": "Your request has been accepted and is being processed"
}
```
```json Webhook payload — verified theme={null}
{
"event_type": "verification.completed",
"request_uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"external_id": "user_98765",
"result": "verified",
"match_score": 0.94,
"deepfake_detected": false,
"latency_ms": 1183,
"created_at": "2025-03-15T14:25:08Z"
}
```
```json Webhook payload — deepfake detected theme={null}
{
"event_type": "verification.completed",
"request_uuid": "d4e5f6a7-b8c9-0123-defa-234567890123",
"external_id": "user_98765",
"result": "rejected",
"match_score": 0.71,
"deepfake_detected": true,
"latency_ms": 1402,
"created_at": "2025-03-15T14:28:44Z"
}
```
# Authentication
Source: https://docs.voxmind.ai/getting-started/authentication
How Voxmind API tokens work, and how to manage them for production use.
Every request to the Voxmind API must include a valid Bearer token in the `Authorization` header. This page explains the token lifecycle — from your first trial token through to production long-lived tokens — and how to manage them safely.
## How authentication works
Voxmind uses **long-lived Bearer tokens** for service-to-service authentication. This is intentional: when you're integrating voice biometrics into a contact centre platform or backend service, you don't want to deal with token refresh flows every hour. Your server gets a token, stores it securely, and uses it for every API call.
```http theme={null}
Authorization: Bearer YOUR_API_TOKEN
```
Every token is scoped to your **organisation**. A token cannot access another organisation's data, voiceprints, or settings.
## Your first token (trial)
When you sign up at [developers.voxmind.ai](https://developers.voxmind.ai), you're automatically issued a **trial token**. This token has a short lifespan — between 15 days and 3 months depending on your plan — and is restricted to sandbox usage. It's designed to let you build and test your integration before committing to a production deployment.
Trial tokens expire. Before you go to production, issue a long-lived token using the API Tokens endpoint and update your configuration. If your trial token expires while you're still building, simply generate a new one from your dashboard.
## Issuing a long-lived production token
When you're ready for production, issue a dedicated token with an appropriate expiry or no expiry at all. Best practice is to give the token a meaningful `friendly_name` so you can identify it later.
```bash theme={null}
curl -X POST https://api.voxmind.ai/organisations/{org_id}/api-tokens \
-H "Authorization: Bearer YOUR_CURRENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"friendly_name": "production-server-01",
"enabled": true
}'
```
The response contains the full `bearer` token string — **this is the only time you will see the raw token value**. Store it immediately in a secrets manager (AWS Secrets Manager, HashiCorp Vault, or at minimum an environment variable in your deployment configuration). Voxmind only stores a hashed version and cannot retrieve it for you.
```json theme={null}
{
"bearer": "eyJjbGllbnRfaWQiOiJZe...",
"token": {
"id": 1,
"friendly_name": "production-server-01",
"enabled": true,
"created_at": "2025-03-01T09:00:00Z"
}
}
```
## Token expiry strategy
By default, tokens issued without an `expires_at` field **do not expire**. This is convenient but creates risk if a token is ever exposed. Voxmind recommends a rotation strategy based on your security posture.
For most integrations, rotating tokens every 90 days strikes the right balance. The rotation process is: issue a new token, update your deployment configuration, verify the new token works correctly, then delete the old one. There's no downtime because both tokens are valid simultaneously during the transition.
```bash theme={null}
# Issue a token that expires in 90 days
curl -X POST https://api.voxmind.ai/organisations/{org_id}/api-tokens \
-H "Authorization: Bearer YOUR_CURRENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"friendly_name": "production-server-01-2025-q2",
"expires_at": "2025-06-01T00:00:00Z",
"enabled": true
}'
```
## Listing and managing your tokens
You can view all active tokens for your organisation at any time. This is useful to audit what's in use, identify tokens that haven't been used recently, and confirm nothing unexpected is active.
```bash theme={null}
curl -X GET https://api.voxmind.ai/organisations/{org_id}/api-tokens \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
To disable a token without deleting it (useful if you suspect a token may be compromised but want to keep the record):
```bash theme={null}
curl -X PATCH https://api.voxmind.ai/organisations/{org_id}/api-tokens/{token_id} \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
```
To permanently remove a token:
```bash theme={null}
curl -X DELETE https://api.voxmind.ai/organisations/{org_id}/api-tokens/{token_id} \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
## Security best practices
Never hardcode tokens in source code or commit them to version control. Use environment variables in development and a secrets manager in production. If you're deploying to AWS (as Voxmind recommends), AWS Secrets Manager with automatic rotation hooks is the cleanest solution and costs pennies per secret per month.
One token per service is a good discipline. If you're running multiple servers or microservices that call the Voxmind API, give each one its own token with a descriptive name. That way, if one service is compromised, you can revoke its token without affecting others.
## Error responses
If your token is invalid, expired, or disabled, you'll receive a `401 Unauthorized` response:
```json theme={null}
{
"code": 401,
"message": "Invalid or expired authentication credentials"
}
```
If your token is valid but you don't have permission to perform a specific action, you'll receive `403 Forbidden`. This typically means the resource doesn't belong to your organisation.
# Environments
Source: https://docs.voxmind.ai/getting-started/environments
Understanding sandbox and production environments in Voxmind.
Voxmind provides two isolated environments so you can build and test your integration safely without affecting real user data or voiceprints.
## Sandbox (test) environment
Your trial token automatically operates in the **sandbox environment**. In sandbox mode, enrollments and verifications are processed through the same ML pipeline as production, so you get realistic accuracy scores and latency — but data is completely isolated from production and will not persist beyond your trial period.
Use the sandbox to test your webhook handler, validate your audio encoding logic, and build confidence in the integration before going live. You won't be charged for sandbox API calls regardless of volume.
```
Base URL (sandbox): https://api.voxmind.ai (same URL, scoped by token type)
Token prefix: vm_test_...
```
## Production environment
Production tokens are issued when you move to a paid plan. All data in production is persistent, encrypted at rest and in transit, and subject to your organisation's data residency settings. Verifications in production go through full SLA guarantees as defined in your subscription.
```
Base URL (production): https://api.voxmind.ai
Token prefix: vm_live_...
```
The base URL is the same for both environments. Voxmind routes your request to the correct environment automatically based on your token type. This means switching from sandbox to production is as simple as swapping the token in your environment configuration — no URL changes required.
## Keeping environments separate
The most common integration mistake is enrolling users in sandbox and then attempting to verify them using a production token. Voiceprints created in sandbox are not accessible in production. Treat them as completely separate databases.
A reliable pattern for managing this in your codebase is to store the API token as a single environment variable (`VOXMIND_API_TOKEN`) and load the appropriate token per deployment environment. Your production deployment gets a `vm_live_` token, your staging deployment gets a `vm_test_` token, and your code never needs to know which environment it's in.
```bash theme={null}
# .env.production
VOXMIND_API_TOKEN=vm_live_eyJjbGllbnRfaWQi...
# .env.staging
VOXMIND_API_TOKEN=vm_test_eyJjbGllbnRfaWQi...
```
# Introduction
Source: https://docs.voxmind.ai/getting-started/introduction
Welcome to Voxmind — deepfake-resistant voice biometrics via a simple API.
## What is Voxmind?
Voxmind is a voice biometrics API that lets you add **real-time voice authentication** and **AI voice clone detection** to any application in hours. Instead of passwords, PINs, or knowledge-based questions, your users simply speak — and Voxmind tells you in under 2 seconds whether the voice is genuine, enrolled, and human.
What makes Voxmind different from traditional voice biometrics is that it was built from the ground up for the era of AI-generated audio. Legacy voice systems were designed when the threat was simple replay attacks. Today, anyone can clone a voice from a 3-second sample using freely available tools. Voxmind's phoneme-level analysis measures anatomical constants of the vocal tract — characteristics that AI voice clones cannot replicate — achieving 99.8% deepfake detection accuracy.
Make your first API call in under 5 minutes.
Understand how API tokens work and how to manage them.
The science behind phoneme-frequency biometrics.
Full endpoint documentation with interactive examples.
## How Voxmind works in 30 seconds
The API follows a simple three-step model that maps naturally to any authentication flow.
First, you **enroll** a user — submit a short voice recording (3–5 seconds is enough) and Voxmind creates a voiceprint tied to your user's ID in your system. Second, when that user needs to authenticate, you **verify** — submit a new recording and Voxmind returns a match score and a pass/fail decision in under 2 seconds. At the same time, a parallel **deepfake detection** check runs automatically, flagging synthetic or replayed audio before it can ever pass verification.
Voxmind is **text-independent and language-agnostic**. Users can say anything in any languages during enrollment and verification — there's no required passphrase.
## Base URL
All API requests are made to:
```
https://api.Voxmind.ai
```
## Who is this for?
If you're building any of the following, Voxmind was designed for you:
* **Contact centre platforms** reducing authentication handle time and stopping account takeover fraud
* **Fintech and banking apps** adding step-up voice verification for high-risk transactions
* **Voice AI agents** that need continuous identity assurance during a conversation
* **Identity verification pipelines** adding a biometric layer to existing KYC flows
* **Employee access systems** using voice as a secure, hardware-free key
## API status
Check real-time API uptime at [status.Voxmind.ai](https://status.Voxmind.ai).
## Need help?
Reach us at [support@Voxmind.ai](mailto:support@Voxmind.ai) or book a technical call via [Voxmind.ai/contact](https://Voxmind.ai/contact).
# Quickstart
Source: https://docs.voxmind.ai/getting-started/quickstart
Enroll a user and verify their voice in under 5 minutes.
This guide walks you through the three core actions — enrolling a user, verifying them, and understanding the response — using real API calls. By the end, you'll have a working voice authentication flow you can adapt for your application.
**Before you begin:** You'll need an API token. If you don't have one yet, [sign up at developers.voxmind.ai](https://developers.voxmind.ai/signup) and your trial token will be emailed to you within a few seconds. The trial token is valid for your sandbox environment and doesn't require a credit card.
## Step 1: Get your organisation ID
Every resource in the Voxmind API is scoped to your **organisation**. Think of it as your tenant identifier — all your users, voiceprints, and settings live under it.
After signup, your organisation ID is included in your welcome email. You can also retrieve it by calling:
```bash theme={null}
curl -X GET https://api.voxmind.ai/organisations/{your_org_id} \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
```
Keep your `org_id` handy — you'll use it in every subsequent request.
## Step 2: Enroll a user
Enrollment creates a voiceprint for a user in your system. You send a voice recording as a binary blob alongside your user's identifier (`external_id` — this is your user's ID in your own database, so you control the format).
```bash cURL theme={null}
curl -X POST https://api.voxmind.ai/organisations/{org_id}/enrollments \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"voice_data": "",
"request_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "user_12345",
"language": "en-UK"
}'
```
```python Python theme={null}
import requests
import base64
import uuid
# Read and encode your audio file
with open("enrollment_audio.wav", "rb") as f:
voice_data = base64.b64encode(f.read()).decode("utf-8")
response = requests.post(
"https://api.voxmind.ai/organisations/{org_id}/enrollments",
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
"Accept": "application/json"
},
json={
"voice_data": voice_data,
"request_uuid": str(uuid.uuid4()), # unique ID for this request
"external_id": "user_12345", # your user's ID
"language": "en-UK"
}
)
print(response.json())
```
```javascript Node.js theme={null}
const fs = require("fs");
const { randomUUID } = require("crypto");
const voiceData = fs.readFileSync("enrollment_audio.wav").toString("base64");
const response = await fetch(
"https://api.voxmind.ai/organisations/{org_id}/enrollments",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
voice_data: voiceData,
request_uuid: randomUUID(), // unique per request
external_id: "user_12345",
language: "en-UK",
}),
}
);
const data = await response.json();
console.log(data);
```
**A successful enrollment returns HTTP 202**, meaning the request was accepted and is being processed asynchronously. The response looks like this:
```json theme={null}
{
"callback_url": "https://voxmind.io/callback_url",
"message": "Your request has been accepted and is being processed"
}
```
**Why async?** Voiceprint generation involves running audio through our ML pipeline. It typically completes in 1–3 seconds but we return immediately so your application doesn't block. Voxmind calls your webhook with the result when ready. See the [Webhooks guide](/guides/webhooks) for setup.
### Audio requirements for enrollment
For best results, the voice recording should be a WAV or MP3 file, at least 3 seconds long (5 seconds is ideal), recorded at a minimum of 16kHz sample rate. The user can say anything — Voxmind is text-independent. Background noise is handled by our preprocessing pipeline, but quieter environments produce more accurate voiceprints.
## Step 3: Verify a user
Once a user is enrolled, you can verify them at any time. The verification call is structurally identical to enrollment — you send a new voice recording with the same `external_id` that was used during enrollment. Voxmind finds their stored voiceprint and compares it.
```bash cURL theme={null}
curl -X POST https://api.voxmind.ai/organisations/{org_id}/verifications \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"voice_data": "",
"request_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"external_id": "user_12345",
"language": "en-UK"
}'
```
```python Python theme={null}
response = requests.post(
"https://api.voxmind.ai/organisations/{org_id}/verifications",
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
"Accept": "application/json"
},
json={
"voice_data": voice_data, # new recording from the auth attempt
"request_uuid": str(uuid.uuid4()),
"external_id": "user_12345", # must match the enrollment external_id
"language": "en-UK"
}
)
```
```javascript Node.js theme={null}
const response = await fetch(
"https://api.voxmind.ai/organisations/{org_id}/verifications",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
voice_data: voiceData,
request_uuid: randomUUID(),
external_id: "user_12345", // same ID used at enrollment
language: "en-UK",
}),
}
);
```
Like enrollment, verification returns HTTP 202 and delivers the result to your webhook. The verification result payload includes a **match score** (0.0–1.0) and a **deepfake flag** indicating whether the audio was detected as synthetic or replayed.
## Step 4: Understand the result
When Voxmind calls your webhook, the payload will contain three key pieces of information: whether the voice matched the enrolled voiceprint, the confidence score for that match, and whether the audio was flagged as a deepfake or replay attack.
Your application logic should combine all three signals. A passing score alone isn't enough — you should reject any verification attempt where `deepfake_detected` is `true`, even if the voice score is technically above your threshold. An attacker using a high-quality voice clone might produce a reasonable match score, and the deepfake flag is your last line of defence.
```json theme={null}
{
"request_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"external_id": "user_12345",
"result": "verified",
"match_score": 0.94,
"deepfake_detected": false,
"latency_ms": 1247
}
```
Always check `deepfake_detected: false` before granting access, regardless of `match_score`. A result of `deepfake_detected: true` should be treated as a security event and logged accordingly.
## What's next?
Now that you have a working enroll/verify flow, here's what to explore next.
Move to the [Authentication guide](/getting-started/authentication) to understand how to manage API tokens properly for production, including how to issue long-lived tokens that don't expire. Then read the [Webhooks guide](/guides/webhooks) to set up your callback endpoint to receive async results. When you're ready to go live, switch from your trial token to a production long-lived token and point requests at the live environment as described in [Environments](/getting-started/environments).
# Contact Centre Integration
Source: https://docs.voxmind.ai/guides/contact-center-integration
How to integrate Voxmind voice authentication into a contact centre environment — covering IVR flows, agent-assisted authentication, and SIP telephony considerations.
Contact centres are the highest-volume use case for voice biometrics. A caller rings in, speaks naturally during the IVR or the opening of an agent conversation, and Voxmind either confirms their identity silently in the background or prompts them for a brief verification phrase. Done well, authentication becomes invisible — customers stop reciting account numbers and security questions, and agents spend less time on identity verification and more time resolving the actual query.
This guide covers the architecture decisions, audio pipeline requirements, and application logic patterns specific to contact centre deployments. It assumes you've already completed the [Quickstart](/getting-started/quickstart) and understand the basic enrollment and verification flow.
## Understanding the telephony audio challenge
The single most important thing to understand about contact centre integration is that telephony audio is deliberately constrained. The public switched telephone network (PSTN), VoIP protocols like SIP, and codec standards like G.711 and G.729 were designed to transmit intelligible speech efficiently — not to preserve the full acoustic richness of a human voice. The result is audio sampled at 8kHz with a frequency response that cuts off above 4kHz, compared to the 16kHz or higher sampling rates that voice biometric models were originally trained on.
This isn't a dealbreaker — Voxmind is explicitly designed and tested against telephony-grade audio — but it does shape some of your integration decisions. In particular, it affects which audio source you capture from, how you handle codec transcoding, and what you communicate to users about acceptable recording conditions.
The most important practical decision you'll make is **where in your telephony stack to capture audio**. You have two main options. The first is capturing from the recording stream of your contact centre platform — most enterprise platforms (Avaya, Genesys, Cisco UCCX, Amazon Connect, and others) expose a call recording API or SIPREC stream that gives you a copy of the audio in near-real time. The second is capturing directly at the IVR layer, where your IVR platform collects the audio and hands it to your backend. The SIPREC approach is generally preferable for agent-assisted authentication because it captures the full conversation naturally. The IVR capture approach is better for passive authentication during self-service flows.
## Enrollment in a contact centre context
Enrollment is the step most integrations underinvest in, and it directly determines the quality of every subsequent verification. In a contact centre deployment, you have a few distinct opportunities to enroll users.
**First-call enrollment** is the most seamless approach for new customers. When a verified customer (authenticated via another method — OTP, password, agent-assisted KBA) calls for the first time, the IVR or agent UI presents a consent prompt and collects 20–30 seconds of natural speech for enrollment. The enrollment audio doesn't need to be a specific phrase — Voxmind's text-independent approach means the IVR can ask the caller to describe their query briefly while simultaneously capturing the enrollment sample.
**Proactive enrollment** is done outside the call itself — for example, via a web or mobile app where the customer explicitly creates their voice profile. This approach gives you better audio quality (no telephony codec degradation), cleaner consent capture, and more control over the enrollment conditions. If your platform has a mobile or web channel, enrolling there and then using that voiceprint to authenticate on future calls is architecturally clean and gives you a better baseline voiceprint.
**In-call silent enrollment** is possible but requires careful UX design. If a caller speaks enough during a single call — typically 30+ seconds of natural speech across the IVR and agent conversation — Voxmind can construct a voiceprint from that audio retrospectively. This is useful for progressively enrolling your existing customer base without an explicit enrollment step, but you must ensure consent was captured before processing begins.
Whatever your enrollment path, the core principle is the same: send audio via `POST /organisations/{orgId}/voice/enroll` with the customer's `external_id`, and store the fact that enrollment is complete in your own CRM or customer database. Voxmind returns `status: enrolled` once enough audio has been processed — at which point every subsequent call by that customer becomes an authentication opportunity.
## Two authentication patterns
### IVR passive authentication
In this pattern, the caller authenticates during the IVR before ever reaching an agent. The IVR captures a short audio sample — typically a spoken account number, date of birth, or a simple free-text response to a standard prompt — and sends it to Voxmind in the background. By the time the caller is routed to an agent, Voxmind has already returned a verification result, and the agent screen-pop can show the authentication status immediately.
The UX flow looks like this: the IVR greets the caller and asks them to state their reason for calling or say their name. Simultaneously, it extracts the `external_id` from the caller's input (account number keypad entry, for example) or from a CRM lookup based on the incoming CLI/ANI. It sends the audio and `external_id` to `POST /organisations/{orgId}/voice/verify`, and listens for the webhook result. If `result: verified` comes back before the call is routed, the agent sees a green authentication indicator. If it comes back after routing, the agent UI updates in real time via a WebSocket push.
```javascript theme={null}
// IVR backend: capture audio, kick off async verification
async function onIvrSpeechCapture(audioBuffer, callerId) {
// Resolve your external_id — could be from ANI lookup, account keypad, or IVR prompt
const externalId = await resolveExternalId(callerId);
if (!externalId) {
// No enrolled user found — route to agent for manual KBA
return routeToAgentWithStatus(callerId, 'NOT_ENROLLED');
}
// Send to Voxmind asynchronously — don't block the IVR on the result
const formData = new FormData();
formData.append('audio', new Blob([audioBuffer], { type: 'audio/wav' }));
formData.append('external_id', externalId);
formData.append('request_uuid', generateUuid()); // Track this for webhook correlation
await fetch('https://api.voxmind.ai/organisations/42/voice/verify', {
method: 'POST',
headers: { Authorization: 'Bearer YOUR_API_TOKEN' },
body: formData,
});
// Route the call — verification result will arrive via webhook
return routeToAgentWithStatus(callerId, 'VERIFICATION_PENDING');
}
// Webhook handler: update agent screen-pop when result arrives
app.post('/webhooks/voxmind', async (req, res) => {
const { request_uuid, external_id, result, match_score, deepfake_detected } = req.body;
res.sendStatus(200); // Acknowledge immediately
if (deepfake_detected) {
await flagCallForFraudReview(external_id, request_uuid);
await pushToAgentUI(external_id, { auth_status: 'DEEPFAKE_DETECTED' });
return;
}
const status = result === 'verified' && match_score >= 0.82
? 'AUTHENTICATED'
: 'FAILED';
await pushToAgentUI(external_id, { auth_status: status, match_score });
});
```
### Agent-assisted authentication
In agent-assisted flows, the agent triggers authentication during the call — typically when a caller requests an action that requires identity verification (a large transaction, account change, or access to sensitive data). The agent clicks an "Authenticate" button in their desktop UI, the system captures the next 10–15 seconds of the caller's speech, and the result appears on the agent's screen.
This pattern is simpler to implement because the agent controls when authentication starts, but it introduces a moment of friction — the caller is typically aware that an authentication check is happening. For high-value interactions this is appropriate and expected. For routine queries, the IVR passive approach is less disruptive.
The backend implementation is identical — `POST /voice/verify`, wait for webhook — but the trigger mechanism is an agent UI action rather than an automatic IVR event.
## Handling the inconclusive result in telephony
Telephony audio is noisier and more variable than web or mobile audio. Background noise in the caller's environment, poor mobile signal, speakerphone degradation, and codec artefacts can all reduce audio quality to the point where Voxmind returns `result: inconclusive` rather than `verified` or `rejected`.
In a contact centre context, `inconclusive` should route to a fallback path rather than a retry. Unlike a web or mobile app where you can ask the user to speak again in a quieter location, a contact centre caller has limited control over their environment. The graceful handling is to present `inconclusive` as a soft failure — the agent authenticates via a secondary method (last four digits of a card, a security question, or a one-time passcode) and notes the inconclusive result for your analytics pipeline. Over time, the inconclusive rate is a useful signal for tuning your audio capture quality.
## SIP and SIPREC considerations
If you're integrating with a SIP-based telephony platform using SIPREC for real-time audio capture, there are a few practical points worth knowing.
First, SIPREC streams are typically G.711 (ulaw or alaw) at 8kHz. Voxmind accepts this natively — you don't need to transcode to a higher sample rate before sending. If your platform offers G.722 (wideband, 16kHz), use it when available as it produces marginally better results, but G.711 is fully supported.
Second, SIPREC delivers audio in two legs — the caller's audio and the agent's audio as separate streams. For verification purposes, you want the caller's audio leg only. Mixing both legs into a single stream before sending to Voxmind will degrade results because the model will be trying to match against a voiceprint that was enrolled from a single-speaker source.
Third, if your platform introduces audio compression or aggressive noise cancellation at the infrastructure level before the SIPREC tap, those processing steps can sometimes alter the spectral characteristics of the audio in ways that affect voiceprint matching. If you're seeing lower match scores than expected in production, the SIPREC tap point is the first thing to investigate.
## Consent and compliance
Contact centre voice authentication deployments involve biometric data collected from callers in a telephony context. Regulatory requirements vary by jurisdiction, but in general you should: disclose to callers that voice biometrics are being used for authentication, obtain consent before the first enrollment, provide a mechanism to opt out (which means offering an alternative authentication path), and retain records of consent in your CRM alongside the Voxmind enrollment status.
In the UK and EU, biometric data processing requires an explicit legal basis under GDPR Article 9. In many US states, the Illinois Biometric Information Privacy Act (BIPA) and similar laws apply. In Australia, the Privacy Act covers biometric data. Voxmind does not make compliance determinations on your behalf — you should consult your legal team about the specific obligations in your operating jurisdictions.
The standard contact centre disclosure is a brief IVR or agent-read statement along the lines of: *"This call may use voice biometrics for authentication and fraud prevention purposes. By continuing, you consent to your voice being used for these purposes."* A caller who withholds consent should be offered an alternative authentication path — typically KBA or OTP — without penalty.
## Recommended match score thresholds for contact centres
Based on typical telephony audio quality and the authentication risk profile of contact centre interactions, a match score threshold of **0.80–0.85** is appropriate for most use cases. High-value transactions — large transfers, account recovery, address changes — warrant a higher threshold of **0.88–0.92**. Routine service calls — balance enquiry, bill payment, appointment booking — can operate at the lower end of the range without meaningful security degradation.
Start at 0.82, monitor your false reject rate (how often enrolled customers fail to authenticate) and false accept rate (how often authentication succeeds for the wrong person, detectable only through fraud analytics), and adjust from there. The right threshold is the one that balances customer friction against your organisation's fraud risk tolerance — there's no universally correct value.
# Deepfake Detection
Source: https://docs.voxmind.ai/guides/deepfake-detection
How Voxmind detects AI-generated voices, replay attacks, and synthetic audio — with a false positive rate under 0.1%.
Of all the things Voxmind does, deepfake detection is the capability most developers want to understand deeply before they put it in front of real users. This guide explains how it works, what it catches, what the numbers mean, and how to build your application logic around it correctly.
## Why deepfake detection is now non-negotiable
Until around 2021, "voice spoofing" mostly meant one thing: someone recorded your voice and played it back to fool a biometric system. Replay attacks are relatively straightforward to detect — recorded audio has characteristic compression artefacts, microphone-room response signatures, and other tell-tale marks that a trained model can identify.
The threat landscape changed when high-quality neural voice cloning became accessible. Tools like XTTS, Tortoise TTS, and dozens of commercially available services can now generate a convincing voice clone from as little as 3 seconds of source audio, in real time, for free. The resulting synthetic audio doesn't have the artefacts of a replay attack — it's freshly generated, at high quality, and acoustically similar enough to the target voice to fool both human listeners and traditional voice biometric systems that weren't designed with this threat in mind.
The FBI's IC3 unit has documented a sharp increase in voice-based social engineering attacks using AI-generated audio, particularly targeting contact centres and financial institutions where voice is used as an authentication factor. Voxmind was built knowing this threat exists. Deepfake detection isn't a feature we added later — it runs on every single verification call, automatically, with no additional integration work required on your part.
## What Voxmind actually detects
Voxmind's deepfake detection catches three distinct categories of attack, and it's worth understanding each one.
**AI voice clones** are the primary modern threat. These are voices generated by neural text-to-speech or voice conversion models that have been conditioned on samples of the target user's voice. The generator models — typically based on diffusion, GAN, or autoregressive architectures — learn to reproduce the acoustic surface of the target voice but cannot replicate the underlying biomechanical relationships that Voxmind's phoneme analysis measures. That's the voiceprint mismatch side. On the deepfake detection side, neural-generated audio carries statistical signatures in the frequency domain that are distinct from human-produced speech — subtle but consistent artefacts in how spectral energy is distributed across frames. Voxmind's AASIST model is specifically trained to identify these signatures across a wide range of synthesis architectures.
**Replay attacks** involve recording authentic audio from the target user — from a phone call, a public video, a voicemail — and playing it back during a verification attempt. Replay attacks produce a different set of artefacts: the acoustic fingerprint of the recording device and playback environment, slight temporal smearing from digital-to-analogue and analogue-to-digital conversion, and characteristic room impulse responses. These are well-understood signals that the detection model identifies reliably.
**Voice conversion attacks** are somewhere in between: a live human voice is run through a real-time conversion model that shifts its characteristics toward the target's voice. This is technically more demanding for an attacker and produces a third distinct artefact profile — the residual characteristics of the source voice bleed through the conversion, and the spectral boundaries between phonemes have a characteristic smoothness that differs from natural speech.
## The technology: AASIST
Voxmind's deepfake detection is built on **AASIST** (Audio Anti-Spoofing using Integrated Spectro-Temporal Graph Attention Networks), a state-of-the-art anti-spoofing architecture that won the ASVspoof 2021 challenge — the leading academic benchmark for voice anti-spoofing. It's worth understanding why AASIST outperforms simpler approaches, because the reason is directly connected to why deepfake audio is hard to detect in the first place.
The core insight of AASIST is that the artefacts left by synthetic audio are not localised in either time or frequency alone. A spectrogram of synthetic speech might look convincing in any given short window — the frequency content is right, the energy distribution looks natural. But the *relationships* between spectral and temporal patterns across the full audio signal tell a different story. Human speech has complex dependencies between what's happening at different time points and different frequency bands simultaneously. Neural synthesis models approximate these dependencies but not perfectly.
AASIST models these relationships using a **graph attention network** where nodes represent different spectro-temporal regions of the audio and edges represent learned relationships between them. The model learns which relationships are diagnostic of authentic versus synthetic speech and attends to them accordingly. This makes it substantially more robust to the kind of adversarial optimisation that can fool simpler classifiers — an attacker who optimises to defeat a frequency-domain classifier can inadvertently fix the temporal artefacts while introducing new graph-level artefacts that AASIST catches.
The practical result is that AASIST generalises well to voice cloning architectures it wasn't explicitly trained on, which matters because the synthesis model landscape is evolving rapidly. You don't want a deepfake detector that only works against last year's cloning tools.
## The numbers: what a sub-0.1% false positive rate means in practice
Voxmind's false positive rate — the rate at which genuine human speech is incorrectly flagged as synthetic — is **under 0.1%**. This is the number that matters most for your integration design, so let's unpack what it actually means for your users.
A false positive rate of 0.1% means that in every 1,000 legitimate verification attempts by real, enrolled users, fewer than 1 will be incorrectly flagged as a deepfake. At a contact centre processing 50,000 calls per month, that's fewer than 50 false deepfake flags across the entire month — about 1-2 per day — all of which can be recovered through a fallback authentication path.
Compare this to false positive rates in the 1–5% range that are common in less specialised approaches: at 1%, that same 50,000-call contact centre would generate 500 false flags per month, which is a meaningful customer experience problem. At a sub-0.1%, it's a manageable edge case rather than a systematic friction point.
The false positive rate was measured against a diverse test set spanning multiple languages, microphone types (mobile, landline, VOIP, headset), acoustic environments (office, home, outdoor, IVR), and demographic groups including speakers with accents, older voices, and voices affected by illness. The number is not cherry-picked from ideal lab conditions.
The false positive rate quoted here is the rate for the deepfake detection check specifically. The overall verification false reject rate — cases where a genuine enrolled user fails verification for any reason including voiceprint mismatch — is a separate metric that depends on your configured match score threshold. See [Enrollment Best Practices](/guides/enrollment-best-practices) for guidance on threshold configuration.
## How the parallel pipeline works
A key design decision in Voxmind is that deepfake detection runs **in parallel** with voiceprint matching, not sequentially. This has two implications worth understanding.
The first is latency: you don't pay an additive time cost for deepfake detection. The total verification time (under 2 seconds) covers both checks running simultaneously on your audio. There's no "deepfake detection mode" to enable — it's always on, always parallel, no trade-off.
The second is independence: the two checks can disagree, and both signals matter. The most important case is when a voice clone produces a non-trivial match score but deepfake detection fires — the attacker's clone is acoustically similar enough to the target to produce a partial voiceprint match, but the AASIST model identifies the audio as synthetic. This is precisely the attack scenario where a deepfake detection check is essential: a match-score-only system might pass this attempt. Voxmind's design treats a positive deepfake detection as a definitive rejection regardless of the match score.
The second interesting case is when deepfake detection is clean (`deepfake_detected: false`) but the match score is below your threshold. This is a normal failed verification — the person speaking is genuinely human, just not the enrolled user. Handle it as a standard authentication failure rather than a security event.
## Building your application logic correctly
The webhook result gives you three signals: `result` (verified/rejected/inconclusive), `match_score` (0.0–1.0), and `deepfake_detected` (boolean). The right decision tree for your application logic is straightforward but it's worth being explicit about the priority order.
The deepfake flag should be evaluated first and should be treated as absolute. If `deepfake_detected` is `true`, the verification attempt is rejected — full stop. Don't apply the match score threshold. Don't offer a retry. Log it as a security event with the `request_uuid`, `external_id`, and timestamp. Whether or not the match score was high, the audio was synthetic, and that's a red flag that warrants investigation rather than a fallback authentication path.
```javascript theme={null}
// Correct priority order in your webhook handler
async function handleVerificationResult(result) {
const { request_uuid, external_id, result: outcome, match_score, deepfake_detected } = result;
// Deepfake check is always evaluated first — no exceptions
if (deepfake_detected) {
await logSecurityEvent({
type: 'DEEPFAKE_DETECTED',
external_id,
request_uuid,
match_score, // Log this too — a high score here is especially notable
timestamp: new Date().toISOString()
});
// Reject. Don't offer voice retry. Offer alternative auth or human review.
return { decision: 'REJECTED', reason: 'synthetic_audio_detected' };
}
// Only evaluate match score once deepfake check is clean
if (outcome === 'verified' && match_score >= YOUR_THRESHOLD) {
return { decision: 'APPROVED' };
}
if (outcome === 'inconclusive') {
// Audio quality too low for reliable determination
// Safe to offer one retry with a request for clearer audio
return { decision: 'RETRY', reason: 'insufficient_audio_quality' };
}
// Clean audio, real human, wrong voice — standard auth failure
return { decision: 'REJECTED', reason: 'voice_mismatch' };
}
```
Notice the separation between `DEEPFAKE_DETECTED` and standard `voice_mismatch` rejections. These are fundamentally different events from a security operations perspective. A voice mismatch might be a genuine user having a bad connection — annoying but benign. A deepfake detection is a potential fraud attempt and should trigger different downstream logic: account flag, fraud team notification, potentially a temporary account lock depending on your risk policy.
## Choosing the right match score threshold
Your match score threshold is a dial between security and convenience. Higher thresholds mean fewer false accepts (better security) but more false rejects (more friction for legitimate users). The right number depends on your use case.
For contact centre authentication replacing a knowledge-based question, a threshold of **0.80–0.85** is typically appropriate. The security improvement over KBA is dramatic regardless of where you set it in this range, and the false reject rate at 0.80 is low enough that the overall authentication experience is meaningfully better than what it replaces.
For step-up authentication on high-value transactions — a large bank transfer, account recovery, changing contact details — a threshold of **0.90–0.92** is more appropriate. Legitimate users making high-stakes requests are typically in a calmer environment with better audio conditions, so the higher threshold has less impact on genuine users while materially raising the bar for an attacker.
For continuous authentication use cases, where verification is running periodically throughout a session, a lower threshold like **0.75** makes sense — false rejects mid-session are very disruptive, and the continuous nature of the monitoring means a single low-confidence frame doesn't determine the outcome. Your application logic should look at the trend across multiple verifications rather than any single result.
## What deepfake detection doesn't cover
Being clear about the boundaries of any security system is more useful than overstating its capabilities.
Voxmind's deepfake detection is designed for audio that has been synthetically generated or replayed. It doesn't solve **social engineering attacks** where an attacker convinces a legitimate user to authenticate on their behalf — no biometric can. It also doesn't protect against scenarios where the attacker has somehow compromised the audio channel between the user's device and your server, bypassing the capture stage entirely. Standard transport security (TLS, certificate pinning on mobile) covers that attack surface separately.
The detection model is retrained periodically as new synthesis architectures emerge. The AASIST approach generalises well, but no detection system is infinitely future-proof against novel synthesis techniques. Voxmind monitors emerging synthesis models and updates the detection model proactively. If you need advance notice when model updates ship, subscribe to the changelog at [docs.voxmind.ai/resources/changelog](/resources/changelog).
## Frequently asked questions
**"What happens if a user genuinely has a very unusual voice that the model hasn't seen before?"** Unusual voices — distinctive accents, speech impediments, very high or low fundamental frequency — affect the voiceprint matching model, not the deepfake detection model. The deepfake detector is evaluating properties of how the audio was generated, not what the voice sounds like. An unusual voice is just as reliably identified as authentic human speech.
**"Can a sophisticated attacker defeat the detection if they know Voxmind is in use?"** The AASIST model is trained on a broad distribution of synthesis artefacts, including scenarios where the attacker is aware of and trying to defeat detection. Adversarial optimisation against frequency-domain features tends to fix those features while introducing artefacts at the graph-relational level. This is an active area of research and Voxmind's model is updated accordingly. The key point is that the bar for defeating the system is substantially higher than for defeating a legacy voice biometric, and the combination of voiceprint matching AND deepfake detection means an attacker needs to beat both simultaneously.
**"Should I tell users that deepfake detection is running?"** Yes, for compliance reasons in most regulated industries. Something simple like "this call may use voice biometrics for authentication and fraud detection" is standard practice and increasingly required by regulation. It also has a deterrent effect — knowing that synthetic audio detection is active discourages lower-sophistication attacks.
# Enrollment Best Practices
Source: https://docs.voxmind.ai/guides/enrollment-best-practices
How to capture and submit voice recordings that produce the most accurate voiceprints.
The quality of a user's initial enrollment directly affects how reliably they'll be authenticated in future verifications. A voiceprint built from a clean, sufficient recording will perform well across varying conditions — different microphones, ambient noise, and the natural day-to-day variation in how someone speaks. A voiceprint built from poor audio will produce inconsistent results and frustrate users who get incorrectly rejected.
This guide covers everything you need to know to capture good enrollment audio from your users.
## Minimum audio requirements
Voxmind accepts WAV and MP3 audio files. The recording should be **at least 3 seconds of clean speech**, with 5 seconds being the sweet spot for optimal voiceprint accuracy. The sample rate should be a minimum of 16kHz — most modern recording APIs on web and mobile default to 44.1kHz or 48kHz, which is fine; Voxmind will downsample internally.
What matters far more than length is the **signal-to-noise ratio**. Three seconds of clean speech in a quiet room produces a better voiceprint than ten seconds of speech with consistent background noise, because the phoneme-frequency extraction pipeline has to work harder to isolate clean phoneme boundaries when noise is present.
## Designing your enrollment UX
The enrollment experience matters for two reasons: it affects audio quality (users who understand what you need will speak more naturally and clearly) and it affects completion rates (users who find the process confusing will abandon it).
**Tell users what to say.** Even though Voxmind is text-independent, users benefit from a prompt. "Please say your full name and confirm today's date" works well — it's natural, generates varied phoneme content, and gives users something specific to focus on rather than feeling like they're talking into the void.
**Use a visual indicator to show recording is active.** A simple animated waveform or countdown timer signals that the system is listening and processing. Without it, users often speak too softly or stop speaking before the required duration.
**Validate before submitting.** Record the audio client-side and do a quick client-side check on duration (is it at least 2 seconds?) and amplitude (is there actually speech present?) before you submit to the API. This catches the common failure modes — user didn't speak, recording was too brief — before wasting an API call.
**Offer a re-enrollment path.** Circumstances change. A user who enrolled on a phone in a quiet environment might need to re-enroll when you build a desktop app. Make it easy to update their voiceprint in your account settings flow.
## Handling the async response
Enrollment returns HTTP 202 (Accepted) immediately and delivers the result to your configured webhook endpoint when processing is complete, typically within 1–3 seconds. Your webhook payload will indicate whether the enrollment was successful and whether the voiceprint quality meets the threshold for reliable verification.
If the enrollment quality score is below the minimum threshold — which can happen with very short audio, very noisy recordings, or audio where no clear speech was detected — Voxmind will flag this in the webhook response. Build your flow to handle this gracefully: rather than silently failing, tell the user the enrollment didn't capture clearly and prompt them to try again.
## Re-enrollment and voiceprint updates
You can submit a new enrollment for an `external_id` at any time. The new recording will replace the existing voiceprint. There is no concept of accumulating multiple enrollments — each user has a single active voiceprint associated with their `external_id` in your organisation.
This is intentional: maintaining a single current voiceprint keeps the matching model simple and avoids the complexity of managing voiceprint versions. If a user's voice characteristics change significantly — which is rare but can happen after surgery, illness, or significant aging — re-enrollment resolves it cleanly.
## Multi-language enrollment
Voxmind is language agnostic and is text-independent, which means a user can enroll in one language and verify in another with no degradation in accuracy. However, for the best voiceprint quality, it's good practice to have users enroll in the language they're most likely to speak during verification. This is a marginal difference rather than a functional one, but it's worth noting for deployments where users speak multiple languages within your application.
See the [Language Support guide](/guides/language-support) for the full list of supported languages and ISO codes.
# Filtering, Sorting & Pagination
Source: https://docs.voxmind.ai/guides/filtering-sorting-pagination
How to query list endpoints effectively using Voxmind's filter, sort, and pagination system.
All Voxmind list endpoints (`GET /organisations/{id}/users`, `GET /organisations/{id}/api-tokens`, etc.) support a consistent query system for filtering results, sorting them, and paginating through large collections. Once you understand the pattern, it applies identically across every list endpoint.
## Filtering
Filtering is controlled via the `filter` query parameter. Multiple filter conditions are comma-separated, and the system evaluates them with an implicit AND — meaning all conditions must be true for a result to be returned.
The basic syntax for equality is `field=value`. For inequality, use `field!=value`. For more advanced comparisons, Voxmind supports a colon-delimited comparator syntax: `field=comparator:value`.
The supported comparators are `gt` (greater than), `gte` (greater than or equal to), `lt` (less than), `lte` (less than or equal to), `like` (case-sensitive pattern match), and `ilike` (case-insensitive pattern match). The `like` and `ilike` comparators support standard SQL-style wildcards: `%` matches any string of zero or more characters, and `_` matches any single character.
Here are some practical examples to illustrate the pattern:
```
# Get all tokens with a specific friendly name prefix
GET /organisations/{id}/api-tokens?filter=friendly_name=like:production%
# Get users created after a specific date
GET /organisations/{id}/users?filter=created_at=gte:2025-01-01T00:00:00Z
# Combine conditions: active users with a specific external ID pattern
GET /organisations/{id}/users?filter=is_active=true,external_id=like:user_%
# Exclude a specific value
GET /organisations/{id}/users?filter=external_id!=deleted_user_123
```
## Sorting
Sorting is controlled via the `sort` query parameter. It uses the same colon-delimiter pattern for direction: `sort=field:direction`. Use `asc` for ascending (A→Z, oldest→newest) and `desc` for descending. Multiple sort keys are comma-separated and applied in order of priority.
```
# Sort by creation date, newest first
GET /organisations/{id}/users?sort=created_at:desc
# Sort by name ascending, then by creation date descending as a tiebreaker
GET /organisations/{id}/users?sort=name:asc,created_at:desc
```
## Pagination
Voxmind uses page/per\_page pagination rather than cursor-based pagination. The `per_page` parameter controls how many results are returned per page (maximum 300, default 100), and the `page` parameter specifies which page of results you want, starting at 1.
The response headers include `X-Total-Pages`, which tells you how many pages exist in total for your query. This lets you build a complete pagination loop without needing to guess when you've reached the end.
```bash theme={null}
# Get the second page of results, 50 per page
GET /organisations/{id}/users?per_page=50&page=2
# Check the X-Total-Pages header in the response
# X-Total-Pages: 7 → there are 7 pages total at 50 items each
```
## Combining all three
The real power comes from combining filtering, sorting, and pagination together to build efficient queries. For example, to retrieve all enabled API tokens ordered by creation date, 20 at a time:
```bash theme={null}
GET /organisations/{id}/api-tokens?filter=enabled=true&sort=created_at:desc&per_page=20&page=1
```
If `X-Total-Pages` returns 3, you'd make two additional requests with `page=2` and `page=3` to retrieve the complete set. A clean pattern for this in application code is to loop while `current_page <= total_pages`, incrementing `page` on each iteration.
# How Voice Biometrics Works
Source: https://docs.voxmind.ai/guides/how-voice-biometrics-works
The science behind Voxmind's phoneme-level voice authentication and deepfake detection.
Understanding how Voxmind processes voice data helps you make better integration decisions — things like why audio quality matters, what "language-agnostic" actually means in practice, and why the system is resilient to the kinds of spoofing attacks that defeat legacy voice biometrics.
## The core problem with traditional voice biometrics
Traditional voice biometric systems work by extracting a voiceprint from the overall acoustic profile of a speech signal — characteristics like pitch, timbre, and spectral envelope. This was sufficient when the threat was simple: someone playing a recording they'd captured of the target user.
The problem is that these acoustic characteristics are exactly what modern AI voice cloning models are trained to replicate. Given a 3-second sample of your voice, a well-resourced attacker can produce synthetic audio that defeats most legacy voice biometric systems, because the clone successfully mimics the surface-level acoustic signature the system was designed to measure.
## What Voxmind measures instead
Voxmind's approach focuses on **phoneme-level analysis** — measuring characteristics that emerge from the physical anatomy of a speaker's vocal tract rather than the acoustic surface of what they're saying.
When you speak, your vocal tract — the shape and configuration of your mouth, tongue, jaw, teeth, and pharynx — acts as a resonant filter that shapes the sound produced by your vocal cords. These anatomical resonances create predictable frequency relationships between different phonemes (the distinct units of sound in speech). The ratio between the resonant frequencies of different phoneme pairs is a function of your physical anatomy, not your speech patterns.
Critically, these ratios are **constants** — they don't change when you're sick, when you age slightly, when you're stressed, or when you're speaking a different language. And they're extremely difficult for a voice clone to replicate, because most voice cloning models are trained to reproduce the acoustic output of a voice, not the underlying biomechanical relationships that produce it.
This is why Voxmind can perform authentication in any language without requiring re-enrollment: the phoneme-frequency relationships don't change with language. French spoken by a native Russian speaker still reveals the same anatomical constants as Russian, because those constants are in the speaker's vocal tract, not in the language they're speaking.
## The processing pipeline
When you submit a voice recording to Voxmind — whether for enrollment or verification — it goes through a five-stage pipeline.
The first stage is **preprocessing**: noise reduction, normalisation, and extraction of the clean speech signal from any background audio. The second stage is **phoneme extraction**: segmenting the audio into its constituent phoneme units using our XLSR-300M-based speech model, which was trained across 128 languages and handles language-agnostic phoneme segmentation. The third stage is **biomarker derivation**: computing the inter-phoneme frequency ratios for each detected phoneme pair across the recording. The fourth stage is **matching or enrollment**: either storing the derived biomarker profile (enrollment) or comparing it against the stored profile for the claimed identity (verification). The fifth stage is **liveness detection**: a parallel check using our AASIST graph attention network that analyses the statistical signatures of AI-generated audio. Synthetic voices, regardless of their acoustic quality, leave detectable artefacts in the frequency domain that this model is trained to identify.
The entire pipeline completes in under 2 seconds for verification on our production infrastructure.
## What this means for your integration
A few practical implications worth understanding before you build.
**Audio quality matters more than audio length.** A clean 3-second recording produces a better voiceprint than a noisy 10-second recording. The phoneme-frequency measurements are disrupted by persistent low-frequency noise in ways that simple acoustic features are not. If your use case involves phone calls, consider applying noise reduction at the capture stage.
**The user doesn't need to say a specific phrase.** Because Voxmind is text-independent, your enrollment and verification UX can be conversational — "please state your name and confirm your date of birth" works just as well as "please say the magic phrase." This is significantly less friction than text-dependent systems that require users to memorise and repeat passphrases.
**Deepfake detection runs automatically.** You don't need to make a separate call to detect synthetic audio — it's included in every verification response. Your application logic should treat `deepfake_detected: true` as a definitive rejection regardless of the match score, and should log it as a potential fraud event.
**The system is resilient to gradual voice change.** Illness, aging, and emotional state affect the acoustic surface of speech but not the underlying phoneme-frequency ratios. Users won't fail verification because they have a cold or because they enrolled five years ago. The identity model is stable across these natural variations.
# Language Support
Source: https://docs.voxmind.ai/guides/language-support
All languages supported by Voxmind's voice biometrics engine.
Voxmind's phoneme extraction model (XLSR-300M) was trained across 128 languages, making the core voice biometrics engine genuinely language-agnostic. This means a user can enroll in one language and authenticate in another — the voiceprint captures anatomical constants that don't change with language.
The `language` parameter on enrollment and verification calls helps the phoneme segmentation model apply the correct acoustic boundaries for the submitted audio. It's optional (defaulting to `en-UK`) but specifying the correct language improves accuracy, particularly for languages with phoneme inventories that differ significantly from English.
## Supported language codes
The `language` field uses `[ISO 639-1]-[ISO 3166-1 alpha-2]` format.
| Language | Code |
| ------------------------ | ------- |
| English (UK) | `en-UK` |
| English (US) | `en-US` |
| French | `fr-FR` |
| German | `de-DE` |
| Spanish (Spain) | `es-ES` |
| Spanish (Latin America) | `es-MX` |
| Italian | `it-IT` |
| Portuguese (Brazil) | `pt-BR` |
| Portuguese (Portugal) | `pt-PT` |
| Dutch | `nl-NL` |
| Polish | `pl-PL` |
| Russian | `ru-RU` |
| Arabic (Modern Standard) | `ar-SA` |
| Hindi | `hi-IN` |
| Mandarin Chinese | `zh-CN` |
| Japanese | `ja-JP` |
| Korean | `ko-KR` |
Additional language support is being expanded continuously. If your use case requires a language not listed, contact [support@voxmind.ai](mailto:support@voxmind.ai) — we can often add support for specific languages on request.
# Mobile App Integration
Source: https://docs.voxmind.ai/guides/mobile-integration
How to integrate Voxmind voice authentication into iOS, Android, and React Native applications — covering microphone capture, background noise handling, security considerations, and UX patterns for mobile-specific contexts.
Mobile is an ideal context for voice biometrics — users are already accustomed to biometric authentication on their phones (Face ID, Touch ID, fingerprint), the microphone hardware is generally high quality, and voice authentication complements rather than competes with the existing biometric stack. For use cases where a user can't look at a screen or use their hands, voice becomes the only viable authentication mechanism.
This guide covers the mobile-specific considerations that differ meaningfully from web and contact centre integrations: platform audio APIs, microphone permissions, background noise, offline resilience, and the security model for keeping your API credentials out of the app binary.
## The mobile security architecture
The same rule that applies to web integrations applies here: your Voxmind API token must never be embedded in your app bundle. APK and IPA files can be decompiled. Strings embedded in compiled code, including those stored as environment variables or constants, are recoverable by a determined attacker. If your token appears in the app, it is effectively public.
The correct architecture is identical to the web pattern — your app sends audio to your own backend, your backend proxies the call to Voxmind with the bearer token attached server-side. The app never touches the Voxmind API directly.
For the app-to-backend leg, use your normal authenticated API calls with your own session token or JWT. This means the chain of trust is: the user authenticates to your app normally (via whatever method you use for non-voice flows), your app receives a session token, and that session token is what authorises the voice enrollment or verification call to your backend. Voxmind then becomes an additional authentication layer on top of your existing auth system, not a replacement for it.
## iOS audio capture
On iOS, the `AVAudioEngine` and `AVAudioRecorder` APIs handle microphone input. For voice biometrics, `AVAudioEngine` gives you more control over the audio pipeline and is the preferred approach for production integrations.
The key iOS-specific settings are the audio session category and the sample rate. You want `AVAudioSessionCategoryRecord` or `AVAudioSessionCategoryPlayAndRecord` with `AVAudioSessionModeVoiceChat` — this mode tells iOS to apply voice-optimised signal processing, including acoustic echo cancellation and noise suppression that is tuned for voice rather than music. The sample rate should be 16kHz for voice biometric capture. iOS natively supports this rate and it gives Voxmind the frequency resolution it needs for reliable phoneme analysis above the telephony-grade 8kHz floor.
```swift theme={null}
import AVFoundation
class VoxMindRecorder {
private var audioEngine = AVAudioEngine()
private var audioBuffer = [Float]()
func requestPermissionAndStart() {
// Always request permission at the moment it's contextually obvious
AVAudioSession.sharedInstance().requestRecordPermission { [weak self] granted in
guard granted else {
// Handle denial gracefully — show instructions for re-enabling in Settings
return
}
DispatchQueue.main.async { self?.start() }
}
}
private func start() {
let session = AVAudioSession.sharedInstance()
do {
// VoiceChat mode applies the signal processing tuned for human speech
try session.setCategory(.record, mode: .voiceChat)
try session.setPreferredSampleRate(16000)
try session.setActive(true)
} catch {
print("AVAudioSession setup failed: \(error)")
return
}
let inputNode = audioEngine.inputNode
// Use the hardware's native format for capture, then resample if needed
let recordingFormat = AVAudioFormat(
standardFormatWithSampleRate: 16000,
channels: 1
)!
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) {
[weak self] buffer, _ in
// Accumulate PCM samples — send to backend when done
let channelData = buffer.floatChannelData![0]
self?.audioBuffer.append(contentsOf: UnsafeBufferPointer(
start: channelData,
count: Int(buffer.frameLength)
))
}
try? audioEngine.start()
}
func stop() -> Data {
audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: 0)
// Convert Float PCM to WAV Data for upload
return convertToWav(samples: audioBuffer, sampleRate: 16000)
}
}
```
## Android audio capture
On Android, the `AudioRecord` API gives you low-level PCM access, or you can use `MediaRecorder` for a simpler integration that outputs directly to a file in a compressed format. For voice biometrics, `AudioRecord` with PCM capture is preferred because it gives you control over the audio pipeline and avoids codec-introduced artefacts before the audio reaches Voxmind.
The critical Android-specific parameter is the `audioSource`. Use `MediaRecorder.AudioSource.VOICE_RECOGNITION` rather than the default `MIC` source. The `VOICE_RECOGNITION` source signals to Android's audio subsystem that this audio is destined for speech processing — on most devices this disables noise suppression and automatic gain control at the hardware level, which sounds counterintuitive but is correct for voice biometrics. You want the raw voice signal, not a pre-processed one, so that Voxmind's own processing can operate on clean input.
```kotlin theme={null}
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import java.io.ByteArrayOutputStream
class VoxMindRecorder {
private val sampleRate = 16000
private val channelConfig = AudioFormat.CHANNEL_IN_MONO
private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
private val bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
private var audioRecord: AudioRecord? = null
private var isRecording = false
fun start() {
// VOICE_RECOGNITION source: raw voice without hardware pre-processing
audioRecord = AudioRecord(
MediaRecorder.AudioSource.VOICE_RECOGNITION,
sampleRate,
channelConfig,
audioFormat,
bufferSize
)
audioRecord?.startRecording()
isRecording = true
}
fun stop(): ByteArray {
isRecording = false
val outputStream = ByteArrayOutputStream()
val buffer = ShortArray(bufferSize)
// Drain remaining audio from the buffer
val bytesRead = audioRecord?.read(buffer, 0, bufferSize) ?: 0
if (bytesRead > 0) {
// Convert shorts to bytes for WAV output
buffer.take(bytesRead).forEach { sample ->
outputStream.write(sample.toInt() and 0xFF)
outputStream.write((sample.toInt() shr 8) and 0xFF)
}
}
audioRecord?.stop()
audioRecord?.release()
return addWavHeader(outputStream.toByteArray(), sampleRate)
}
}
```
## React Native
If you're using React Native, the `expo-av` library (for Expo-managed projects) or `react-native-audio-recorder-player` (for bare React Native) are the standard choices. Both expose the platform's native audio APIs under the hood, so the same iOS and Android considerations above apply — you're just invoking them through a JavaScript bridge.
The most important React Native-specific consideration is making sure you're not capturing through a library that resamples audio to 8kHz before handing it to you. Some older React Native audio libraries default to telephony-grade sample rates. Always verify the sample rate of the audio blob before sending to your backend — a file that's labelled as 16kHz but was actually captured at 8kHz is common enough to check for explicitly.
```javascript theme={null}
import { Audio } from 'expo-av';
async function startRecording() {
const { granted } = await Audio.requestPermissionsAsync();
if (!granted) throw new Error('Microphone permission denied');
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true, // Important: without this, iOS mutes recording in silent mode
});
const { recording } = await Audio.Recording.createAsync({
android: {
extension: '.wav',
outputFormat: Audio.AndroidOutputFormat.DEFAULT,
audioEncoder: Audio.AndroidAudioEncoder.DEFAULT,
sampleRate: 16000,
numberOfChannels: 1,
bitRate: 128000,
},
ios: {
extension: '.wav',
audioQuality: Audio.IOSAudioQuality.HIGH,
sampleRate: 16000,
numberOfChannels: 1,
bitRate: 128000,
linearPCMBitDepth: 16,
linearPCMIsBigEndian: false,
linearPCMIsFloat: false,
},
web: {}, // Handled by web-app-integration guide
});
return recording;
}
```
## Background noise on mobile
Mobile users authenticate in highly variable environments — walking down the street, in a coffee shop, on public transport, in a car with road noise. This is the single biggest quality challenge for mobile voice authentication. You cannot control the environment, so you have to handle it in your application logic.
The most important mitigation is **real-time audio level feedback during capture**. Show the user a volume indicator while they're recording. If the level is low, show a prompt: "Speak closer to your phone." If the level is high and variable — suggesting significant background noise — consider showing a warning: "We're picking up a lot of background noise. For best results, try in a quieter location." This doesn't prevent the user from proceeding, but it sets expectations and reduces frustration when verification fails.
Voxmind's multi-stage noise pipeline handles compound noise well — the combination of spectral subtraction, phone-specific normalisations, and XLSR-300M's exposure to diverse audio conditions during training means it performs significantly better in noisy environments than traditional MFCC-based approaches. But no system performs as well in a construction site as in a quiet room. The `inconclusive` result is your signal that the audio quality was insufficient for a reliable determination — handle it as a prompt to retry rather than a failure.
## Offline and low-connectivity considerations
Mobile networks are unreliable. A verification call that takes 2 seconds on wifi might time out on a poor 3G connection, or the HTTP request might fail entirely mid-upload. Build retry logic into your mobile client for audio upload failures.
A reasonable retry strategy: attempt the upload, wait up to 15 seconds for a response, and if it fails or times out, retry once with a fresh recording prompt rather than resending the original audio. Stale audio from a retry of a failed attempt is worse than a fresh sample from a new recording — the user may have moved to a different environment, and the act of asking them to try again often produces better audio anyway.
If your app has offline or low-connectivity use cases, voice authentication will need a fallback path. Design your authentication flow with the assumption that any given verification attempt may fail to reach the server, and make the fallback path (PIN, biometric, OTP) easily accessible without treating it as an error state.
## Platform-specific permission patterns
Both iOS and Android require microphone permission to be requested at runtime, but the UX patterns differ between platforms.
On iOS, you get exactly one chance to show the native permission prompt. If the user denies it, you cannot show it again — they have to manually re-enable microphone access in the Settings app. This means you should make your pre-permission explanation as clear and compelling as possible before triggering the system prompt. A brief in-app screen that explains why voice authentication needs the microphone, shown immediately before the system prompt, meaningfully increases acceptance rates. If the user has previously denied permission, detect this state using `AVAudioSession.recordPermission` and show a custom UI that deep-links them directly to your app's settings page.
On Android, users can deny permission without permanently revoking it, and you can re-request after explaining why you need it (once — repeated requests are blocked after two denials). Use `shouldShowRequestPermissionRationale()` to determine whether to show an explanation before re-requesting.
In both cases, the in-app explanation before the system prompt is the same message: voice authentication requires the microphone, it's only used for authentication, and no audio is stored beyond the processing required to create the voiceprint.
## Passive voice authentication on mobile
An advanced pattern worth considering for high-value mobile apps is **passive voice authentication** — running verification in the background during a voice call or voice input, without the user taking any explicit authentication action. This is distinct from active authentication where the user is explicitly prompted to speak.
For example: a banking app that initiates a voice call for customer support could silently capture the first 15 seconds of conversation, run verification against the enrolled voiceprint, and either confirm identity silently (surfacing a "Verified" indicator to the support agent's screen) or flag for step-up authentication if the score is low. The user experiences a natural conversation, not an authentication checkpoint.
This pattern requires careful consent design — users must be informed that passive authentication is running, typically disclosed in the app's terms and privacy notice and confirmed during the enrollment step. The technical implementation is identical to active verification; the difference is purely in the UX layer and consent model. Consult your legal team before deploying passive authentication in any jurisdiction with biometric data regulations.
# Web App Integration
Source: https://docs.voxmind.ai/guides/web-app-integration
How to integrate Voxmind voice authentication into a browser-based application — covering microphone capture, audio quality, enrollment UX, and client-server architecture.
Web applications present a different set of integration constraints compared to telephony or mobile. The audio quality ceiling is higher — browsers can capture at 16kHz or above, well above the 8kHz telephony floor — but the diversity of hardware (cheap built-in laptop microphones versus high-quality headsets) and environments (open-plan offices, coffee shops, home setups) creates a wide quality range you need to handle gracefully. This guide covers everything from browser microphone capture to UX patterns and the client-server architecture that keeps your API token secure.
## The fundamental architecture rule: never expose your API token in the browser
Before any implementation detail, one rule that is non-negotiable: your Voxmind API token must never appear in browser-side JavaScript. If a user opens DevTools and can find your bearer token in network requests, JavaScript bundles, or environment variables, anyone on the internet can impersonate your users using that token.
The correct architecture is for your backend to act as a proxy for all Voxmind API calls. Your browser-side code captures audio and sends it to your own server endpoint. Your server then forwards the audio to Voxmind's API, attaches the secret bearer token server-side, receives the result, and returns an appropriate response to the browser — without ever exposing the raw Voxmind response or your credentials to the client.
```
Browser Your Server Voxmind API
│ │ │
│── POST /auth/enroll ──────>│ │
│ (audio blob + user info) │ │
│ │── POST /voice/enroll ─>│
│ │ (with bearer token) │
│ │<── 200 OK ─────────────│
│<── { status: "enrolled" } ─│ │
```
This pattern also gives you a natural place to add your own business logic — rate limiting, fraud heuristics, audit logging, consent verification — before audio reaches Voxmind.
## Capturing audio in the browser
The Web Audio API and `MediaRecorder` are the two primary browser tools for voice capture. `MediaRecorder` is the simpler path for most integration scenarios and is supported in all modern browsers.
The key parameters to get right are the MIME type and the sample rate. Voxmind accepts PCM WAV and common compressed formats including MP3 and AAC, but for best results in a web context, capture as `audio/wav` or `audio/webm;codecs=opus`. Opus is a high-quality codec that works well at 16kHz and is natively supported by Chrome, Firefox, and Edge. Safari requires `audio/mp4` as a fallback.
```javascript theme={null}
// A clean, reusable audio capture utility for enrollment and verification
class VoxMindAudioCapture {
constructor() {
this.mediaRecorder = null;
this.chunks = [];
this.stream = null;
}
async start() {
// Request microphone access — the browser will show a native permission prompt
this.stream = await navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 16000, // 16kHz: optimal for voice biometrics
channelCount: 1, // Mono: all we need, halves file size
echoCancellation: true, // Reduce room echo
noiseSuppression: true, // Reduce background noise
autoGainControl: true, // Normalise volume across different microphones
},
});
// Determine the best supported MIME type for this browser
const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus')
? 'audio/webm;codecs=opus'
: 'audio/mp4'; // Safari fallback
this.mediaRecorder = new MediaRecorder(this.stream, { mimeType });
this.chunks = [];
this.mediaRecorder.addEventListener('dataavailable', (e) => {
if (e.data.size > 0) this.chunks.push(e.data);
});
this.mediaRecorder.start(100); // Capture in 100ms chunks for streaming if needed
}
stop() {
return new Promise((resolve) => {
this.mediaRecorder.addEventListener('stop', () => {
const blob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType });
// Clean up the microphone stream so the browser's recording indicator disappears
this.stream.getTracks().forEach((track) => track.stop());
resolve(blob);
});
this.mediaRecorder.stop();
});
}
}
```
One practical detail: call `stream.getTracks().forEach(track => track.stop())` as soon as you've finished recording. If you don't, the browser's microphone-in-use indicator (the red dot or camera icon in the browser tab) will stay active, which erodes user trust. Stop the stream as soon as the blob is captured, every time.
## Enrollment UX patterns
Enrollment is the only moment where you have to ask something slightly unusual of a user: *"Say something for a few seconds so we can learn your voice."* How you frame this interaction significantly affects completion rates.
The most effective framing ties the enrollment request to an immediate, concrete benefit the user can see. Instead of explaining voice biometrics, show the user what they're getting: *"Enable voice ID to sign in with your voice next time — no password needed."* Present enrollment as an optional feature with clear value, not as a compliance step. Completion rates are markedly higher when enrollment is opt-in and clearly labelled as making the user's life easier.
For the recording itself, aim for at least 20 seconds of natural speech. You can collect this in one of two ways. The first is a **scripted passage** — you show the user a sentence or two to read aloud. This is predictable and easy to implement, and it gives you consistent audio length and content. The second is **prompted free speech** — you ask the user a simple question ("Describe what you plan to use this account for") and let them respond naturally. Free speech enrollment produces a voiceprint that reflects more natural voice variation, which generally improves later verification performance. Either approach works; scripted is easier to implement, prompted free speech produces better long-term results.
Always show a real-time audio level visualiser during recording. Users with no visual feedback don't know whether the microphone is working. A simple waveform or volume bar built on `AnalyserNode` from the Web Audio API is enough — the point is to confirm to the user that their voice is being captured, and to give them immediate feedback if their microphone level is too low.
```javascript theme={null}
// Minimal volume visualiser using Web Audio AnalyserNode
function createVolumeMonitor(stream, onVolumeChange) {
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
source.connect(analyser);
const data = new Uint8Array(analyser.frequencyBinCount);
function tick() {
analyser.getByteFrequencyData(data);
// Average energy across frequency bins — 0 to 255 range
const volume = data.reduce((a, b) => a + b, 0) / data.length;
onVolumeChange(volume); // Pass to your UI component to drive the visualiser
requestAnimationFrame(tick);
}
tick();
return () => audioContext.close(); // Return cleanup function
}
```
## Sending audio to your backend proxy
Once recording is complete, send the audio blob to your backend via a standard multipart POST. Your backend then forwards it to Voxmind. The important thing is that this is a single-direction call from the browser's perspective — the user doesn't wait for the Voxmind webhook. You return a `202 Accepted` to the browser immediately, and your backend processes the webhook result asynchronously.
```javascript theme={null}
// Browser-side: send to your backend proxy
async function submitEnrollmentAudio(audioBlob, userId) {
const form = new FormData();
form.append('audio', audioBlob, 'enrollment.webm');
form.append('user_id', userId);
const response = await fetch('/api/voice/enroll', {
method: 'POST',
body: form,
// No Content-Type header — the browser sets it with the correct boundary for FormData
});
if (!response.ok) throw new Error('Enrollment submission failed');
return response.json(); // { status: 'processing', request_uuid: '...' }
}
```
```javascript theme={null}
// Server-side proxy (Node.js/Express): forward to Voxmind
app.post('/api/voice/enroll', authenticate, async (req, res) => {
const { userId } = req.body;
const audioFile = req.files.audio;
const form = new FormData();
form.append('audio', audioFile.buffer, {
filename: 'enrollment.webm',
contentType: audioFile.mimetype,
});
form.append('external_id', userId);
form.append('request_uuid', generateUuid());
const response = await fetch(
`https://api.voxmind.ai/organisations/42/voice/enroll`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VOXMIND_API_TOKEN}`, // Server-side only
...form.getHeaders(),
},
body: form,
}
);
const result = await response.json();
res.status(202).json({ status: 'processing', request_uuid: result.request_uuid });
});
```
## Handling the webhook result in a web context
Voxmind sends verification results to your webhook endpoint — a server-to-server call that happens asynchronously after the user's audio is processed. In a web application, this creates the challenge of getting that asynchronous result back to the browser in near real-time without the user having to refresh.
The two standard approaches are WebSocket connections and server-sent events (SSE). WebSockets are bidirectional and appropriate if you need real-time updates for other features in your application. SSE is simpler to implement and perfectly adequate for this use case — the server only needs to push one event to the browser (the verification result), not maintain ongoing two-way communication.
A practical pattern: when the user submits audio for verification, your server returns a `request_uuid`. The browser opens an SSE connection to a `/api/voice/verify/stream?request_uuid=xxx` endpoint. Your webhook handler receives the Voxmind result, looks up which SSE connection is waiting on that `request_uuid`, and pushes the result. The browser receives the event and updates the UI.
```javascript theme={null}
// Browser: open SSE connection after submitting audio
async function verifyVoice(audioBlob) {
const { requestUuid } = await submitVerificationAudio(audioBlob);
return new Promise((resolve, reject) => {
const sse = new EventSource(`/api/voice/verify/stream?request_uuid=${requestUuid}`);
sse.addEventListener('result', (event) => {
sse.close();
resolve(JSON.parse(event.data));
});
sse.addEventListener('error', () => {
sse.close();
reject(new Error('Verification stream error'));
});
// Timeout after 30 seconds — something went wrong upstream
setTimeout(() => {
sse.close();
reject(new Error('Verification timed out'));
}, 30000);
});
}
```
## Microphone permission UX
Browsers require explicit user permission to access the microphone. The permission prompt appears the first time you call `getUserMedia`. There are a few things worth handling carefully here.
First, request microphone permission at the moment it's contextually obvious why you need it — not on page load, and not buried in an onboarding flow. Request it at the exact moment the user clicks "Record your voice." Users are significantly more likely to grant permission when they initiated the action that requires it.
Second, handle the `NotAllowedError` that comes back if the user denies permission. Show a clear message explaining that voice ID requires microphone access, and include instructions for re-enabling it in their browser settings. Don't just show a generic error — users who accidentally denied permission are often willing to re-enable it if you explain why it's needed and how to do it.
Third, on HTTPS: `getUserMedia` only works on secure origins. In production this is a non-issue — you're already serving over HTTPS. In development, use `localhost` (which browsers treat as a secure origin) rather than an IP address or `http://` URL, otherwise your microphone capture code won't work at all.
## Audio quality checks before sending
Rather than sending audio blindly and handling a poor-quality result from Voxmind, you can do a lightweight client-side quality check before submission. Two checks are worth implementing. The first is a minimum duration check — if the recorded audio is under 5 seconds, it's almost certainly too short for reliable verification, and you can prompt the user to try again before the round trip to your server. The second is a minimum volume check — if the average audio level from your `AnalyserNode` was below a threshold throughout the recording, the microphone may be muted or the user was speaking too quietly. Both checks can be done in milliseconds client-side and save you unnecessary API calls.
```javascript theme={null}
function isAudioUsable(blob, averageVolume, durationSeconds) {
if (durationSeconds < 5) {
return { ok: false, reason: 'Recording too short — please speak for at least 5 seconds.' };
}
if (averageVolume < 15) { // 0–255 scale from AnalyserNode
return { ok: false, reason: 'Microphone level too low — check your microphone is not muted.' };
}
if (blob.size < 10000) { // Less than 10KB is suspiciously small for 5+ seconds of audio
return { ok: false, reason: 'Audio data appears incomplete — please try again.' };
}
return { ok: true };
}
```
These are fast, cheap checks that meaningfully improve the user experience when things go wrong — particularly for users on devices with muted microphones or browser permission issues.
# Webhooks
Source: https://docs.voxmind.ai/guides/webhooks
Receiving async results from Voxmind enrollment and verification calls.
Voxmind processes enrollments and verifications asynchronously — you submit a request, receive an immediate 202 Accepted, and then Voxmind calls your server with the result when processing is complete. This guide explains how to set up your webhook endpoint, what payloads to expect, and how to handle them reliably.
## Why async?
Running a voice recording through phoneme extraction, biomarker derivation, voiceprint matching, and deepfake detection is compute-intensive. The entire pipeline completes in under 2 seconds — but that's 2 seconds of meaningful processing time, not a round-trip that should block a synchronous HTTP connection. The async pattern keeps your application fast and lets Voxmind handle load spikes without affecting your API response times.
## Setting up your webhook endpoint
Your webhook endpoint is a POST handler on your server that Voxmind will call with the result payload. You configure the callback URL in your organisation settings.
Your endpoint must:
* Accept POST requests with a JSON body
* Return HTTP 200 quickly (before any heavy processing on your end)
* Be accessible from Voxmind's servers (i.e., not behind a VPN or firewall that blocks inbound traffic)
A minimal Node.js example using Express:
```javascript theme={null}
// Minimal webhook handler
app.post('/webhooks/voxmind', express.json(), (req, res) => {
// Return 200 immediately so Voxmind knows we received it
res.sendStatus(200);
// Process the result asynchronously
const result = req.body;
handleVoxMindResult(result).catch(console.error);
});
async function handleVoxMindResult(result) {
const { request_uuid, external_id, result: outcome, match_score, deepfake_detected } = result;
if (deepfake_detected) {
// Log as security event, reject the attempt
await flagFraudAttempt(external_id, request_uuid);
return;
}
if (outcome === 'verified' && match_score >= 0.85) {
// Grant access - complete the authentication flow
await completeAuthentication(external_id, request_uuid);
} else {
// Failed verification
await handleFailedVerification(external_id, request_uuid);
}
}
```
The key principle here is **respond first, process second**. If your webhook handler does heavy database work before returning 200, Voxmind may time out waiting for the response and retry. Always return 200 immediately and handle the payload in a background process or async function.
## Matching results to requests
Every enrollment and verification call includes a `request_uuid` that you provide in the request body. Voxmind includes this same UUID in the webhook payload. This is how you tie a webhook callback to the specific API call that triggered it.
Store the `request_uuid` in your database when you make an API call, then look it up when the webhook arrives. This is the most reliable way to correlate results with the user sessions or transaction flows that initiated them — don't rely on timing or order of arrival.
## Enrollment result payload
```json theme={null}
{
"event_type": "enrollment.completed",
"request_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "user_12345",
"status": "success",
"voiceprint_quality_score": 0.91,
"created_at": "2025-03-15T14:22:31Z"
}
```
If `status` is `failed`, the `voiceprint_quality_score` will be below threshold (typically below 0.5) and you should prompt the user to re-enroll with better audio quality.
## Verification result payload
```json theme={null}
{
"event_type": "verification.completed",
"request_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"external_id": "user_12345",
"result": "verified",
"match_score": 0.94,
"deepfake_detected": false,
"latency_ms": 1247,
"created_at": "2025-03-15T14:25:08Z"
}
```
The `result` field will be one of `verified`, `rejected`, or `inconclusive`. An inconclusive result typically indicates audio quality too low for a reliable determination — treat it the same as a rejection and prompt the user to try again.
## Retry behaviour
If your webhook endpoint is unavailable or returns a non-200 status code, Voxmind will retry delivery with exponential backoff — at 30 seconds, 5 minutes, 30 minutes, and 2 hours. After 4 failed attempts the event is dropped. This means a brief server outage won't cause you to permanently lose results, but extended downtime will. Design your infrastructure accordingly.
## Local development and testing
Testing webhooks in local development requires exposing your local server to the internet. [ngrok](https://ngrok.com) is the standard tool for this — it creates a public HTTPS tunnel to your localhost. Run `ngrok http 3000` and use the generated URL as your callback URL in your Voxmind organisation settings during development.
Alternatively, many teams prefer to test webhook handling against a local Voxmind API mock using their preferred HTTP mocking tool, so they can iterate without needing a real webhook each time.
# Changelog
Source: https://docs.voxmind.ai/resources/changelog
A record of all notable changes to the Voxmind API and developer platform — new features, improvements, deprecations, and breaking changes.
This changelog follows [Semantic Versioning](https://semver.org/) for the API. Breaking changes increment the major version and are always communicated with a minimum 60-day deprecation notice. Non-breaking additions and improvements increment the minor version. Bug fixes increment the patch version.
To receive change notifications by email, enable changelog alerts in your [account settings](https://developers.voxmind.ai/settings/notifications).
***
## v1.1.0 — 2025-03-01
**Deepfake detection now included on all verification calls.**
AASIST-based deepfake detection is now enabled by default across all plans and all verification calls. The `deepfake_detected` field in webhook results and verification responses reflects the result of this check. No integration changes are required to receive the signal — if you are already handling webhook results, `deepfake_detected` will now always be present in the payload.
We recommend auditing your webhook handler to ensure you are evaluating `deepfake_detected` before your match score threshold logic. See the [Deepfake Detection guide](/guides/deepfake-detection) for the correct decision tree and application logic patterns.
***
## v1.0.0 — 2024-12-02
**General Availability.**
Voxmind API v1.0.0 is generally available. The v1 API surface is stable — breaking changes will be communicated with a minimum 60-day deprecation period and will increment the major version number.
The v1 GA release includes: voice enrollment and verification (text-independent, language-agnostic), asynchronous webhook delivery, API token management, user and organisation management, and the Billing API for plan and subscription retrieval.
***
## Deprecation policy
When a feature, endpoint, or behaviour is deprecated, it will continue to function for a minimum of 60 days after the deprecation announcement. Deprecation notices appear in this changelog under a **Deprecated** heading and are also sent by email to the technical contact on your account. After the deprecation window closes, the feature is removed in the next major version release.
## Breaking change history
No breaking changes have been introduced since v1.0.0 GA.
# Error Codes
Source: https://docs.voxmind.ai/resources/error-codes
A complete reference for Voxmind API error responses and how to handle them.
Voxmind uses standard HTTP status codes and returns structured JSON error bodies so you can programmatically handle failure cases in your integration.
## Error response format
Every error response follows this structure:
```json theme={null}
{
"code": 400,
"message": "Human-readable description of what went wrong",
"errors": [
{
"field": "voice_data",
"message": "This field is required"
}
]
}
```
The top-level `code` mirrors the HTTP status. The `errors` array is present on validation errors (400) and contains field-level detail about what failed, which is useful for building error messaging in your UI.
## HTTP status codes
**200 OK** — The request completed successfully. Used for GET and PATCH responses.
**201 Created** — A resource was successfully created. Used when issuing a new API token.
**202 Accepted** — Your enrollment or verification request was accepted and is being processed. This is not a success or failure — the actual result will arrive at your webhook endpoint. You should store the `request_uuid` from your request body to match it when the webhook arrives.
**400 Bad Request** — The request failed validation. This usually means a required field is missing, a field value is in the wrong format, or a filter query contains a syntax error. The `errors` array in the response body will identify which field failed and why.
**401 Unauthorized** — Your API token is invalid, expired, or disabled. Check that you're including the correct `Authorization: Bearer YOUR_TOKEN` header and that the token hasn't been revoked in your dashboard.
**403 Forbidden** — Your token is valid but you don't have permission to perform this operation. This typically means you're attempting to access a resource (user, voiceprint, settings) that belongs to a different organisation. Double-check your `org_id` path parameter.
**404 Not Found** — The requested resource doesn't exist. This could mean the `external_id` hasn't been enrolled yet, a token ID doesn't exist, or a user record has been deleted. Make sure your IDs are correct and that the resource exists in the environment your token is scoped to (sandbox vs. production).
**409 Conflict** — A uniqueness constraint was violated. The most common case is attempting to create a predefined text with a language and text\_code combination that already exists. Update the existing resource instead of creating a new one.
**503 Service Unavailable** — The Voxmind API is temporarily unavailable. Check [status.voxmind.ai](https://status.voxmind.ai) for real-time status. Implement exponential backoff with retry logic for 503 responses in production-critical flows.
## Common failure patterns and fixes
**"Invalid or expired authentication credentials" on every request** — Your token has expired or been disabled. Issue a new token from your dashboard or via the API if you still have a valid token to authenticate with.
**202 returned but webhook never fires** — First check that your callback URL is correctly configured in your organisation settings. Then check that your webhook endpoint is publicly accessible (not behind a VPN), returns 200, and is not timing out before responding. Test it with a tool like ngrok and a manual POST to confirm it's reachable.
**Match scores consistently low for a specific user** — The most common cause is poor enrollment audio. Have the user re-enroll in a quieter environment with a recording of at least 5 seconds. If the issue persists across multiple re-enrollments, check that the audio encoding matches (bit depth, sample rate) between enrollment and verification recordings.
**`deepfake_detected: true` for real users** — This is rare but can occur with highly compressed audio or very low-bitrate recordings that create artefacts the deepfake model interprets as synthetic. Ensure your audio pipeline preserves at least 16kHz quality and hasn't applied aggressive compression codecs. If this is happening consistently for legitimate users, contact support with sample recordings.
# Rate Limits
Source: https://docs.voxmind.ai/resources/rate-limits
API rate limits by plan and how to handle them gracefully.
Voxmind enforces rate limits to ensure consistent performance across all customers. Limits are applied per organisation and reset on a monthly basis aligned to your billing cycle.
## Limits by plan
| Plan | Monthly included calls | Requests per second | Concurrent requests |
| -------------- | ---------------------- | ------------------- | ------------------- |
| Free (sandbox) | 100 | 2 | 2 |
| Starter | 2,500 | 10 | 5 |
| Growth | 10,000 | 30 | 10 |
| Scale | 50,000 | 100 | 25 |
| Enterprise | Custom | Custom | Custom |
"Calls" refers to enrollment and verification requests. API token management, user management, and organisation settings calls are not metered and not subject to monthly limits.
## Rate limit headers
Every API response includes headers telling you your current usage state:
```
X-RateLimit-Limit: 2500
X-RateLimit-Remaining: 1847
X-RateLimit-Reset: 1743465600
```
`X-RateLimit-Reset` is a Unix timestamp indicating when your monthly allowance resets. Monitor `X-RateLimit-Remaining` in your integration to build proactive handling before you hit the limit, rather than reacting to 429 errors.
## When you exceed your limit
If you exceed your monthly included calls, one of two things happens depending on your plan. On the Starter, Growth, and Scale plans, additional calls are billed at the overage rate for your tier — your service continues without interruption. On the Free (sandbox) plan, API calls return HTTP 429 once the 100-call limit is reached.
```json theme={null}
{
"code": 429,
"message": "Monthly call limit exceeded. Upgrade your plan or wait for your limit to reset."
}
```
## Handling 429 gracefully
For real-time authentication flows (contact centre, login flows), hitting a rate limit mid-operation is a poor user experience. The right approach is to monitor the `X-RateLimit-Remaining` header as part of your application health metrics and alert your ops team when it drops below a threshold — say, 20% of monthly limit remaining — before you reach zero.
For batch operations (bulk re-enrollment, historical data processing), implement exponential backoff when you receive a 429. A simple strategy is to wait 2 seconds, then 4, then 8, capping at 60 seconds between retries.
# Security
Source: https://docs.voxmind.ai/resources/security
How Voxmind handles your data, protects your API credentials, and approaches the security of biometric information — from data residency to key management to penetration testing.
Security documentation for a voice biometrics platform needs to serve two audiences simultaneously. CTOs and procurement teams need a clear, honest overview of data handling, compliance posture, and the principles that govern how biometric data is stored and processed — enough to make a trust decision. Security engineers and architects need the technical specifics: how keys are managed, what the audit trail looks like, what the network perimeter is, and what scope is available for your own penetration testing programme. This page is structured to serve both. The overview sections come first; the technical depth is below.
## Data handling principles
Voxmind processes audio to create and match voiceprints. Understanding what data is retained, where it lives, and for how long is the starting point for any security or compliance evaluation.
**Audio files are not retained.** When you submit audio for enrollment or verification, Voxmind processes that audio to extract the voiceprint representation and then discards the raw audio. Voxmind does not build a library of your users' voice recordings. The only thing that persists after enrollment is the mathematical voiceprint — a set of extracted acoustic features that cannot be trivially reversed into intelligible speech. This is a deliberate architectural choice, not just a privacy nicety: retaining raw audio would create a much larger attack surface and a significantly more sensitive data store. Discarding it immediately reduces Voxmind's data footprint to the minimum required for authentication.
**Voiceprints are stored against your `external_id`, not against personal data.** Voxmind has no knowledge of your users' names, email addresses, phone numbers, or any other personally identifiable information. The voiceprint is associated only with the opaque `external_id` string you provide. Your application is responsible for the mapping between `external_id` and real user identity. This means that even in the event of a breach of Voxmind's voiceprint store, the extracted data could not be trivially linked to real individuals without access to your own CRM — a significant mitigation against the impact of any hypothetical data exposure.
**Data residency.** Voxmind's production infrastructure is hosted in AWS eu-west-1 (Ireland), with all customer data stored and processed within the EU. This is relevant for GDPR compliance — data does not leave the EU in normal operation. If your use case requires UK-specific data residency or a dedicated single-tenant deployment, contact us to discuss enterprise deployment options.
**Retention and deletion.** Voiceprint data is retained for as long as the user account is active in your system. When you call `DELETE /organisations/{orgId}/users/{userId}`, the voiceprint is permanently deleted from Voxmind's systems within 24 hours. This deletion is irreversible and is the correct mechanism for fulfilling GDPR right-to-erasure requests for voice biometric data. There is no recovery path after deletion — if a user is deleted in error, they will need to re-enroll.
## API credential security
Your Voxmind bearer token is the key to your organisation's voice authentication service. Compromising it would allow an attacker to enroll arbitrary voiceprints, run verifications against your enrolled users, or extract user metadata from your account. Treat it accordingly.
The fundamental rule is simple: the token lives on your server, not in your application code or client-side assets. For web integrations, this means proxying all Voxmind API calls through your backend. For mobile integrations, the same applies — the token never appears in your app bundle. This is covered in detail in the [Web App Integration](/guides/web-app-integration) and [Mobile App Integration](/guides/mobile-integration) guides.
Voxmind's API token management follows the same security principles as the token value itself. When a token is issued via `POST /organisations/{orgId}/api-tokens`, the bearer value is shown exactly once — Voxmind stores only a cryptographic hash, not the raw value. If you lose the token, you issue a new one and revoke the lost one. There is no recovery mechanism, which means there is also no mechanism for an attacker to social-engineer a token recovery.
Tokens support human-readable `friendly_name` labels, expiry dates, and immediate revocation via a single API call or dashboard toggle. We recommend a 90-day rotation schedule for production tokens and immediate rotation if you have any reason to believe a token may have been exposed. The rotation process takes under a minute: issue a new token, update your server's environment variable, revoke the old token, done.
## Biometric data classification
Voiceprints are biometric data under GDPR Article 9 (special category data), the Illinois Biometric Information Privacy Act (BIPA), and equivalent regulations in other jurisdictions. Voxmind is the data processor; your organisation is the data controller. This means:
The consent obligation rests with you. Voxmind provides the technical capability; you are responsible for obtaining and recording lawful consent from users before their audio is enrolled. The minimum required disclosure is that voice biometrics are being used for authentication, what data is retained, and the user's right to have that data deleted.
Your privacy notice should reference voice biometric processing. If you're subject to GDPR, your Record of Processing Activities (RoPA) should include the voice authentication processing activity, with Voxmind listed as a sub-processor. Voxmind's Data Processing Agreement (DPA) is available on request from your account manager.
The deletion pathway is the user's right to erasure. When a user requests deletion of their biometric data, the API call `DELETE /organisations/{orgId}/users/{userId}` fulfils that request — Voxmind permanently removes the voiceprint within 24 hours and you remove the enrollment record from your own system. Document this workflow in your data subject rights procedure.
***
## Technical security reference
*This section is aimed at security engineers and architects who need technical specifics for their own risk assessments, security architecture documentation, and penetration testing programmes.*
### Transport security
All communication with Voxmind's API occurs over TLS 1.2 or higher. TLS 1.0 and 1.1 are not accepted. The minimum cipher suite is AES-128-GCM; AES-256-GCM is preferred and negotiated by default with modern clients. Certificate pinning is not enforced at the API level but is available for enterprise deployments where a fixed certificate is required by your security policy.
For webhook delivery from Voxmind to your server, the same TLS standards apply — Voxmind will reject webhook endpoints that present self-signed certificates or certificates with known weak ciphers. Your webhook endpoint must present a valid certificate from a publicly trusted CA.
### Data at rest
Voiceprint data at rest is encrypted using AES-256. Encryption keys are managed using AWS KMS with automatic annual rotation. Customer data is logically isolated at the database level — queries cannot return records belonging to another organisation regardless of the API token used, as `org_id` scoping is enforced at every data access layer, not only at the API gateway.
### Audit logging
Every API call, authentication event, and administrative action is logged with a timestamp, the requesting token ID, the `external_id` where applicable, the IP address of the calling server, and the result. These logs are retained for 12 months and are available to you in aggregate via the dashboard's analytics views. For specific event lookup by `request_uuid`, the webhook result payload contains all the fields needed for your own audit trail — we recommend persisting webhook results in your own database for independent audit purposes rather than relying solely on Voxmind's logs.
Events that always appear in the audit log include: token issuance, token revocation, user creation (enrollment), user deletion, verification attempts (with result and match score), and deepfake detection flags. There is no mechanism to delete audit log entries — the log is append-only.
### Authentication and authorisation model
Voxmind uses HMAC-SHA256 signed JWT tokens as the bearer credential. When a token is issued, the signing secret is generated with 256 bits of entropy using a cryptographically secure random number generator. The token payload includes the `org_id` as the subject, the audience (`PUBLIC_API`), and an optional expiry. Voxmind verifies the signature on every API request before processing — there is no session state server-side.
Authorisation is organisation-scoped: a valid token for organisation A cannot read, write, or delete data belonging to organisation B. This isolation is enforced at the application layer and at the database layer (row-level scoping on all tables). There is currently a single permission level per token — all tokens for an organisation have the same access scope. Fine-grained token permissions (read-only tokens, enroll-only tokens) are on the product roadmap.
### Rate limiting and abuse prevention
The API enforces rate limits per token to prevent brute-force attacks against enrolled voiceprints. The verification endpoint is rate-limited at 60 requests per minute per token, with a burst allowance of 10 requests per second. Requests that exceed the rate limit receive a `429 Too Many Requests` response with a `Retry-After` header. Sustained high rates of verification failures from a single token trigger an alert to Voxmind's fraud monitoring team and may result in temporary token suspension pending investigation.
If your legitimate use case requires higher rate limits — for example, a contact centre processing a high call volume — contact your account manager to discuss adjusted limits. Enterprise plans include custom rate limit configurations.
### Penetration testing
You are permitted and encouraged to conduct penetration testing against your own Voxmind API integration. The scope for your testing includes: the API endpoints under your organisation's `orgId`, the authentication and authorisation mechanisms for your own tokens, the webhook delivery endpoint on your own server, and the audio processing pipeline as exercised by your enrolled users.
The scope explicitly excludes: any attempt to access data belonging to other organisations, any testing against Voxmind's infrastructure layer (AWS resources, databases, internal services), any denial-of-service testing, and any attempt to extract or reverse-engineer voiceprint data. These restrictions are consistent with AWS's penetration testing policy, which governs the underlying infrastructure.
If your security programme requires a full penetration test report from Voxmind's own security team, or access to our most recent third-party penetration test results, contact your account manager. Enterprise customers have access to Voxmind's penetration test reports under NDA.
### Vulnerability disclosure
If you discover a security vulnerability in Voxmind's API or infrastructure, please report it to [security@voxmind.ai](mailto:security@voxmind.ai) with a description of the issue, reproduction steps, and your contact details. We aim to acknowledge all reports within 24 hours and provide a resolution timeline within 72 hours. We operate a responsible disclosure policy — we ask for a reasonable timeframe to address critical issues before public disclosure, and we recognise researchers who report vulnerabilities responsibly.