API keys and authentication
Use a server-side API key to access the public API reference. Dashboard routes use a separate login token.
Base URL
The production Voice AI API base URL is:
https://api.tunk.ai/agent/v1Append the endpoint path once: /agents becomes https://api.tunk.ai/agent/v1/agents. For local development, use your backend port, for example http://localhost:3001/v1.
Speech-to-Text uses the separate base URL https://api.tunk.ai/v1. The /agent/v1 prefix applies to all Voice AI endpoints, including webhooks, agents, calls, batches, phone numbers, knowledge base and usage.
Create a key
Open Developer Hub → API Keys, generate a key, and store it in your server's environment. Select the workspace whose resources you want to access and copy its workspace UUID.
Required headers
| Header | When to include it | Value |
|---|---|---|
X-Api-Key | Every public API request | Your API key |
X-Workspace-Id | Workspace-scoped requests | The workspace UUID; include it in all examples |
Idempotency-Key | Every public API POST | A unique value for each logical operation |
Content-Type | JSON request bodies | application/json |
Keys bound to a workspace can supply that scope automatically. A conflicting workspace header is rejected. For keys without a workspace binding, provide X-Workspace-Id.
The backend also accepts tunk-api-key, x-tunk-api-key, or raw Authorization: tunk-.... Authorization: Bearer <API_KEY> is not accepted by this public API. Use X-Api-Key to avoid confusion with dashboard JWT authentication.
Verify access
export TUNK_BASE_URL='https://api.tunk.ai/agent/v1'
export TUNK_API_KEY='<your-api-key>'
export TUNK_WORKSPACE_ID='<workspace-uuid>'
curl --fail-with-body "$TUNK_BASE_URL/agents?page=1&limit=20" \
-H "X-Api-Key: $TUNK_API_KEY" \
-H "X-Workspace-Id: $TUNK_WORKSPACE_ID"Idempotency
Send a fresh Idempotency-Key for each new POST, including webhook creation, test delivery and secret rotation. Reuse that value when retrying the same request. A fresh test event needs a fresh key.
Completed responses with status below 500 are cached for 24 hours by API key, request path, and idempotency key. Reuse the same body when retrying; the middleware does not compare bodies. This is response replay, not a guarantee of exactly-once execution: simultaneous requests or Redis failures can still repeat work. Do not send concurrent duplicate mutations.
Troubleshooting
| Response | Check |
|---|---|
401 | Missing, invalid or inactive API key; a Bearer API key was used |
403 | Workspace access or a workspace-bound key/header mismatch |
411 | Missing workspace scope |
400 with IDEMPOTENCY_KEY_REQUIRED | Missing Idempotency-Key on a public POST |
400 with VALIDATION_ERROR | Request fields do not match the reference |
429 | Rate limits; slow down and retry with backoff |
Keep keys private
Store keys in a secret manager or ignored .env file. Never include real keys in browser code, public examples, screenshots, or committed Postman collections. Revoke compromised keys in the API Keys manager.
Continue with the API quickstart or webhook guide.