Viktor Public API
Create Viktor threads, run tasks, continue conversations, and download result files from your own applications.
Use the Viktor Public API to give Viktor work from your own applications and automations. The API is asynchronous: create a thread with a message, track the resulting run, then fetch the final result.
- Base URL: all endpoints live under
https://api.viktor.com/api/public/v1. - Credits: API runs consume workspace credits like any other Viktor task. See how credits work and pricing.
- MCP: Prefer the Viktor MCP server when your caller is an MCP-compatible agent.
- OpenAI or Anthropic SDKs: Use the compatible chat API to drive Viktor with existing SDK code.
Quickstart
1. Create an API key
Open Settings → API Keys, select Generate Key, and grant only the scopes your integration needs. Personal keys act as you. Team keys are for shared automations and can be created by team admins.
The secret is shown once. Store it in a secret manager and never put it in source control or client-side code. Keys begin with zt_live_sk_.
2. Check the key
Send the key as a Bearer token. The x-api-key header is also supported.
export VIKTOR_API_KEY="zt_live_sk_..."
curl "https://api.viktor.com/api/public/v1/me" \
-H "Authorization: Bearer $VIKTOR_API_KEY"GET /api/public/v1/me returns the key type, granted scopes, and active rate-limit policies. It requires no scope, so it is the best first request after creating or rotating a key.
3. Give Viktor a task
Creating a thread also queues its first agent run. Use an idempotency key so a retry cannot start the same task twice.
curl -X POST "https://api.viktor.com/api/public/v1/threads" \
-H "Authorization: Bearer $VIKTOR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: monthly-revenue-2026-07" \
-d '{
"message": "Analyze this month'\''s revenue and summarize the biggest changes.",
"speed": "smarter"
}'The API returns 202 Accepted with thread.id, message.id, and run.id. Save the run ID.
speed selects the agent configuration: smarter (the default) maximizes quality, while faster trades some depth for lower latency on simple tasks.
4. Wait for the result
Poll the run until its status is terminal: completed, requires_action, failed, cancelled, or timed_out.
requires_action means Viktor finished the run by asking you for something — missing input, a decision, or an approval. The result is available like a completed run: read it, then answer by sending a follow-up message on the same thread.
curl "https://api.viktor.com/api/public/v1/runs/<run_id>" \
-H "Authorization: Bearer $VIKTOR_API_KEY"When result.available is true, fetch the final result:
curl "https://api.viktor.com/api/public/v1/runs/<run_id>/result" \
-H "Authorization: Bearer $VIKTOR_API_KEY"Results can contain Markdown, structured JSON, and file artifacts. To continue the same conversation, send another message to the existing thread.
curl -X POST "https://api.viktor.com/api/public/v1/threads/<thread_id>/messages" \
-H "Authorization: Bearer $VIKTOR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: monthly-revenue-follow-up-1" \
-d '{"message":"Turn the analysis into a one-page executive brief."}'Endpoint reference
| Method | Endpoint | Purpose | Required scopes |
GET | /api/public/v1/me | Identify the key and inspect scopes and rate limits | None |
GET | /api/public/v1/test | Lightweight authentication check | None |
POST | /api/public/v1/threads | Create a thread and start a run | threads:create, runs:create |
GET | /api/public/v1/threads | List threads | threads:read |
GET | /api/public/v1/threads/{thread_id} | Get a thread | threads:read |
POST | /api/public/v1/threads/{thread_id}/messages | Continue a thread and start a run | messages:create, runs:create |
GET | /api/public/v1/threads/{thread_id}/messages | List user-visible messages | messages:read |
GET | /api/public/v1/threads/{thread_id}/runs | List runs in a thread | runs:read |
GET | /api/public/v1/runs/{run_id} | Get run status | runs:read |
GET | /api/public/v1/runs/{run_id}/result | Get a terminal run result | runs:read |
POST | /api/public/v1/runs/{run_id}/cancel | Cancel an in-flight run | runs:create |
GET | /api/public/v1/files/{file_token}/download-url | Get a short-lived artifact download URL | files:read |
GET | /api/public/v1/credits | Get the workspace credit balance and runout forecast | usage:read |
GET | /api/public/v1/usage/summary | Get windowed credit usage totals by spend bucket | usage:read |
GET | /api/public/v1/usage/daily | Get per-day credit usage for a window | usage:read |
The full interactive reference — every parameter, schema, response body, and a downloadable spec for generated clients — lives in the dashboard under Settings → API Docs.
Scopes
threads:create— create new API threads.threads:read— read and list API threads.messages:create— create messages in API threads.messages:read— read messages in API threads.runs:create— trigger and cancel agent runs.runs:read— read and list run status and results.files:read— read file metadata and download result artifacts.chat:completions— use the OpenAI- and Anthropic-compatible chat API.usage:read— read workspace-wide credit usage and the credit balance.
A key with no scopes can only call /me and /test. Scope changes take effect on new requests immediately.
Workspace usage and credits
The usage endpoints report workspace-wide spend in credits — the same numbers, buckets, and cron attribution as the dashboard's Usage page. Because that data covers the whole workspace, visibility follows the dashboard's admin rule: team keys with usage:read qualify as-is (only workspace admins can create them or edit their scopes), while a personal key also requires its owner to currently be a workspace admin — otherwise requests fail with 403 admin_role_required.
Windows are selected with either a named period (today, last_7_days, last_30_days, this_month, last_month, current_billing_period — the default — or previous_billing_period) or an explicit start_time/end_time pair of at most 366 days. Days are bucketed in UTC unless you pass an IANA timezone.
GET /credits needs no window: it returns the current balance across all credit pools, the billing period, and a burn-rate runout forecast — useful to check before dispatching a batch of runs instead of discovering an empty balance through 402 errors.
Structured output
Set response_format to a JSON Schema when your application needs a predictable object instead of Markdown. Viktor returns the validated value in the result's json field. The complete response_format schema is in the API reference.
Pagination
Thread and run lists use keyset pagination. Pass the last item's ID as starting_after. Message lists use after.
Files
Run results return file artifacts as tokens, not permanent public URLs. Exchange an artifact's id at /api/public/v1/files/{file_token}/download-url. The returned presigned URL is valid for about 15 minutes.
Rate limits and retries
Limits apply per route and per key owner: all personal keys of the same user share one quota, and all team keys of the same team share one quota, so creating additional keys does not increase throughput. A 429 response includes Retry-After and X-RateLimit-* headers. Wait for the advertised interval before retrying.
Use Idempotency-Key on requests that start runs. Reusing the same key and request returns the originally accepted operation instead of spending credits on a duplicate.
Errors
Errors use one JSON envelope:
{
"detail": {
"error": "machine_readable_code",
"message": "Human-readable explanation"
}
}Some errors add fields such as scope or retry_after_seconds. Treat the machine-readable error value as stable application logic and the message as display text.