TVH Voice docs

#Recipe: Authenticate

Every request needs an API key in the Authorization header. Keys are created in the dashboard (API keys) or with POST /v1/api-keys (admin scope), are shown once, are stored hashed, carry scopes, and can be rotated (POST /v1/api-keys/{id}/rotate) or revoked.

Keep keys on your server. Browsers get short-lived softphone credentials instead (recipe 03).

#TypeScript (SDK)

// Every request carries an API key. Create keys in the dashboard (API keys) and keep them server-side.
import { createVoiceClient } from '@voice/sdk';

const voice = createVoiceClient({ baseUrl: process.env.VOICE_API_URL!, apiKey: process.env.VOICE_API_KEY! });

const { data: workspace, error } = await voice.GET('/v1/workspace');
if (error) throw new Error(`${error.error.code}: ${error.error.message}`);
console.log('workspace', workspace.name, 'sip domain', workspace.sip_domain);
console.log('allowed destination countries', workspace.limits.allowed_destination_countries);

#curl

# Any endpoint; a missing or wrong key returns 401 with error.code UNAUTHENTICATED.
curl -sS "$VOICE_API_URL/v1/workspace" -H "Authorization: Bearer $VOICE_API_KEY"

#Notes

Response Meaning
401 UNAUTHENTICATED Missing, malformed, expired or revoked key
403 FORBIDDEN The key lacks the scope (details names it)
429 RATE_LIMITED Too many requests; wait Retry-After seconds

This example is executed against the sandbox in CI (tests/e2e/specs/m6-docs.spec.ts).