ImportantThese docs are for developers with valid API keys. Requests without a key (or with an invalid key) will fail. Never expose your API key in client-side code.
Integrate Keywave in 3 Calls.
Production flow: Get phrase → Enroll (once) → Verify (with phrase). Voice templates are protected; raw audio is never stored in Keywave.
BillingUsage is billed per verification. See Pricing.
API Keys & Auth
All endpoints require an API key via x-api-key. Keys should be kept server-side only (never ship to browsers or mobile apps).
Required headers
x-api-key: <YOUR_API_KEY>Content-Type: application/json
Rate limitsLimits are enforced per API key and defended by per-IP protections. 429 responses include retry_after. Contact support@keywave.io for higher limits or SLAs.
Quickstart (Recommended Flow)
1) Get a one-time phrase, 2) Enroll a voice template (once), 3) Verify with phrase.
1) POST /api/v1/phrase
Returns phrase_id, prompt, and expires_at. The phrase is single-use and expires after a short TTL.
curl -s https://api.keywave.io/api/v1/phrase \
-H "Content-Type: application/json" \
-H "x-api-key: <YOUR_API_KEY>" \
-d '{"wallet":"<WALLET_ID>"}' | jq
# => { "ok": true, "phrase_id":"pc_xxx", "prompt":"violet harbor seven four", "expires_at":"2025-10-07T12:34:56Z" }2) POST /api/v1/enroll
Create/update a protected voice template from a public or signed audio URL (m4a/mp3/wav/webm).
curl -s https://api.keywave.io/api/v1/enroll \
-H "Content-Type: application/json" \
-H "x-api-key: <YOUR_API_KEY>" \
-d '{
"wallet":"<WALLET_ID>",
"audioUrl":"https://<your-signed-or-public>/voice/enroll.m4a",
"projection_id":"p0",
"params_version":"v1"
}' | jq3) POST /api/v1/verify (phrase required)
Submit a new recording of the prompted phrase. Phrase must be unexpired and unused.
curl -s https://api.keywave.io/api/v1/verify \
-H "Content-Type: application/json" \
-H "x-api-key: <YOUR_API_KEY>" \
-d '{
"wallet":"<WALLET_ID>",
"audioUrl":"https://<your-signed-or-public>/voice/verify.m4a",
"projection_id":"p0",
"params_version":"v1",
"phrase_id":"pc_xxx"
}' | jq
# => { "ok":true, "success":true, "score":0.82, "phrase_match":true, "mode":"audio-template+phrase" }Legacy Hash Flow (Deprecated)
Historical hash-only endpoints remain for backward compatibility but are not recommended.
# Enroll legacy hash
curl -s https://api.keywave.io/api/v1/enroll -H "Content-Type: application/json" -H "x-api-key: <YOUR_API_KEY>" \
-d '{"wallet":"<WALLET_ID>","phrase_hash":"<SHA256_HEX>"}' | jq
# Verify legacy hash
curl -s https://api.keywave.io/api/v1/verify -H "Content-Type: application/json" -H "x-api-key: <YOUR_API_KEY>" \
-d '{"wallet":"<WALLET_ID>","probe_hash":"<SHA256_HEX>"}' | jqHTTP Status by Endpoint
/v1/phrase
- 200 — issued { phrase_id, prompt, expires_at }
- 401 — invalid/missing API key
- 429 — rate limited (see
retry_after) - 500 — server/db error
/v1/enroll
- 200 — created/updated
- 400 — missing
walletor invalidaudioUrl - 401 — invalid/missing API key
- 429 — rate limited
- 500 — server/db error
/v1/verify
- 200 — result payload
- 400 —
MISSING_PHRASE|PHRASE_EXPIRED|PHRASE_ALREADY_USED - 401 — invalid/missing API key
- 404 — no voice template
- 429 — rate limited
- 502 — ASR error upstream
- 500 — server/db error
Concrete Error Examples
{ "ok": false, "code": "MISSING_PHRASE", "message": "phrase_id required for verification", "step":"validate" }{ "ok": false, "code": "PHRASE_EXPIRED", "message": "Challenge expired", "step":"phrase_check" }{ "ok": false, "code": "PHRASE_ALREADY_USED", "message": "Challenge already used", "step":"phrase_check" }{ "ok": false, "code": "RATE_LIMIT", "message": "Too many requests", "retry_after": "2025-10-07T12:00:00Z" }Monitoring & Abuse Protection
- Per-key quotas with per-IP shields; logs stored in
verify_logs. - Alerts on elevated 5xx, p95 latency, or unusual request bursts.
- Keys can be rotated at any time from your console. If leaked, revoke immediately.
References
- OpenAPI / Swagger JSON: /openapi.json
- Changelog: /docs/changelog
- Status page: coming soon
Powered by Keywave