llms.txt

Viktor - AI Employee for Slack and Microsoft Teams

Viktor is an autonomous AI employee that lives in Slack or Microsoft Teams, connects to 3,200+ tools, and does real work: analytics, automation, reports, code, and web apps.

Raw markdown for any blog post is available at: https://viktor.com/blog//md Raw markdown for any research post is available at: https://viktor.com/research//md

Pages

Docs

Getting Started with Viktor

URL: https://viktor.com/docs/getting-started Summary: Add Viktor to your Slack or Microsoft Teams workspace and start automating tasks in minutes.

Viktor is an AI coworker that lives in your Slack or Microsoft Teams workspace. It connects to 3,200+ business tools and does real work — pulling reports, managing campaigns, writing code, and automating workflows.

Add Viktor to Slack or Microsoft Teams

  1. Go to app.viktor.com/signup and create your account.
  2. Click We use Slack or We use Teams and authorize Viktor for your workspace.
  3. Viktor will appear as a member of your workspace — you can message it directly or invite it to channels.

Connect your tools

Viktor works best when it can access the tools your team uses. Head to the Viktor dashboard and connect integrations like:

Each integration authenticates via OAuth — one click per tool, no API keys to paste.

Start working

Message Viktor the way you would message a coworker:

Viktor figures out which tools to use, pulls the data, and delivers the result right in your conversation.

What's next


Connect Your Tools

URL: https://viktor.com/docs/connect-your-tools Summary: Link the apps your team uses so Viktor can do real work in them — one secure OAuth sign-in per tool.

Viktor does his best work when he can reach the tools your team already uses. Every integration connects through the tool's own secure sign-in — no API keys to paste, and you can revoke access at any time.

Connect an app

  1. Open the Integrations screen and find the app you want.
  2. Select Connect. A secure sign-in window opens for that app.
  3. Sign in and approve the access Viktor requests.

Once connected, the app shows as active on the Integrations screen and Viktor can use it in conversations, scheduled tasks, and API runs.

What to connect first

Start with the tools where your work actually lives:

With 3,200+ integrations available, most of your stack is already covered. Browse the full catalog on the integrations page.

Tool not in the catalog?

Viktor can also connect to custom MCP servers and APIs, and can build the missing integration from a tool's API documentation. See request a new integration for how to get one added.

Managing access


Scheduled Tasks

URL: https://viktor.com/docs/scheduled-tasks Summary: Have Viktor run recurring work automatically — daily briefs, weekly reports, monthly reconciliations.

Anything Viktor can do once, he can do on a schedule: a metrics brief every weekday morning, a pipeline report every Monday, an invoice reconciliation on the first of the month.

Set up a schedule

  1. Describe the task to Viktor in Slack or Teams and ask him to run it on a schedule.
  2. Be specific about timing — for example, "every weekday at 9 AM, post yesterday's revenue summary in #finance."
  3. Confirm the schedule when Viktor plays it back, and he saves it as a recurring task.

There is no separate builder to learn. The schedule is part of the conversation, and you can change it the same way: "move the daily brief to 8 AM" or "pause the weekly report until next month."

Ideas to start with

Manage scheduled tasks

  1. Open the Usage section to see your scheduled tasks and the credits they use.
  2. Pause or remove tasks you no longer need.
  3. Adjust the timing whenever your routine changes.

Recurring tasks consume credits on each run, so review them occasionally to keep usage efficient. See how credits work for details.


Viktor Public API

URL: https://viktor.com/docs/public-api Summary: 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.

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, the workspace's rate-limit tier, and the active rate-limit policies scaled to that tier. 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
POST /api/public/v1/scripts/runs Run a Python script directly in the workspace sandbox scripts:execute
GET /api/public/v1/scripts/runs/{script_run_id} Get a script run's status and result scripts:execute
POST /api/public/v1/scripts/runs/{script_run_id}/cancel Cancel a queued script run scripts:execute
GET /api/public/v1/integrations List connected integrations and their SDK modules integrations:read
GET /api/public/v1/integrations/{sdk_module}/tools List an integration's SDK tools with schemas and snippets integrations:read
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
GET /api/public/v1/audit/events Pull workspace audit events, cursor-paginated (team keys only) audit: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

A key with no scopes can only call /me and /test. Scope changes take effect on new requests immediately.

Script runs

Sometimes your own code is the orchestrator and you do not want an agent. Script runs execute one Python script directly in your workspace sandbox. Your script keeps full control of the logic, and Viktor supplies the sandbox, the connected integrations, and the file plumbing.

The script runs as the key owner's identity. It sees the same workspace files the same person sees in chat, and its integration calls pass the same permission checks.

Discover what a script can call

List the connected integrations your key can use and the SDK module each one is imported from:

curl "https://api.viktor.com/api/public/v1/integrations" \
  -H "Authorization: Bearer $VIKTOR_API_KEY"
{
  "integrations": [
    { "sdk_module": "mcp_linear", "name": "Linear", "kind": "mcp", "account_label": null, "tool_count": 12 },
    { "sdk_module": "pd_gmail", "name": "Gmail", "kind": "pipedream", "account_label": "ops@example.com", "tool_count": 31 }
  ]
}

Then list one integration's tools. Every tool comes back exactly as the sandbox SDK exposes it: the function name, the parameters with types and required flags, whether a call needs human approval, and a runnable snippet you can submit to POST /scripts/runs as-is:

curl "https://api.viktor.com/api/public/v1/integrations/mcp_linear/tools" \
  -H "Authorization: Bearer $VIKTOR_API_KEY"
{
  "name": "linear_create_issue",
  "sdk_module": "mcp_linear",
  "description": "Create a Linear issue.",
  "requires_approval": false,
  "parameters": [
    { "name": "title", "type": "str", "required": true, "description": "Issue title", "default": null }
  ],
  "snippet": "import asyncio\n\nfrom sdk.tools.mcp_linear import linear_create_issue\n\n\nasync def main() -> None:\n    result = await linear_create_issue(\n        title=...,  # str\n    )\n    print(result)\n\n\nasyncio.run(main())\n"
}

The discovery set matches execution: a personal key sees the connections its owner can use in chat, and a team key without a chat identity does not see access-restricted connections. Built-in modules (default_tools, utils_tools, ...) vary per workspace; a script can list /work/sdk/tools/ to discover them in place.

Start a run

Start a run with the script source inline. Short scripts can return their result in one round trip with wait_seconds:

curl -X POST "https://api.viktor.com/api/public/v1/scripts/runs" \
  -H "Authorization: Bearer $VIKTOR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sync-crm-2026-08-28" \
  -d '{
    "code": "print(\"hello from the sandbox\")",
    "timeout_seconds": 120,
    "wait_seconds": 30
  }'

The response is the run object. While the run is queued or in_progress, poll it:

curl "https://api.viktor.com/api/public/v1/scripts/runs/<script_run_id>" \
  -H "Authorization: Bearer $VIKTOR_API_KEY"

A terminal run carries the result: the exit code, captured stdout and stderr, and artifacts for output files. Statuses are queued, in_progress, completed, failed, cancelled, and timed_out.

Rules and limits:

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 URL is served from Viktor's API domain, requires no further authentication, and 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.

Limits scale with your workspace plan. Every workspace resolves to a rate-limit tier — free, entry, mid, high, or enterprise — and higher tiers get proportionally higher limits on every route. Read your tier and the exact values that apply to your keys from GET /api/public/v1/me (rate_limit_tier and limits); a plan change takes effect within a few minutes. Upgrading your plan is the supported way to raise your limits.

A 429 response includes Retry-After and X-RateLimit-* headers, plus a plan_tier field in the error detail. 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.


Viktor MCP Server

URL: https://viktor.com/docs/mcp-server Summary: Connect an MCP-compatible agent to Viktor with streamable HTTP and a scoped Viktor API key.

The Viktor MCP server lets any MCP-compatible agent delegate work to Viktor. It exposes the same threads, runs, results, and files as the Viktor Public API, with two agent-friendly tools that can start a task and wait for its result in one call.

Connect

1. Create a scoped API key

Open Settings → API Keys and generate a personal or team key. For the simplest ask_viktor workflow, grant:

Add other scopes only when the MCP client needs the corresponding tools. The server advertises only tools allowed by the key's current scopes.

2. Add the remote server to your MCP client

Every client needs the same two pieces of information: the server URL and an authentication header. Either header style works:

Claude Code

claude mcp add --transport http viktor https://api.viktor.com/mcp \
  --header "Authorization: Bearer $VIKTOR_API_KEY"

Cursor

Add the server to ~/.cursor/mcp.json (or the project's .cursor/mcp.json):

{
  "mcpServers": {
    "viktor": {
      "url": "https://api.viktor.com/mcp",
      "headers": {
        "Authorization": "Bearer <VIKTOR_API_KEY>"
      }
    }
  }
}

Other clients

Most MCP clients accept the same mcpServers JSON shape as the Cursor example, or offer a settings screen where you enter the URL and header. If your client asks for a transport, choose Streamable HTTP, not the legacy SSE transport.

Use your client's secret or environment-variable support instead of saving a live key directly in a shared configuration file.

3. Verify the connection

Call whoami. It requires no scope and returns the key type, granted scopes, the workspace's rate-limit tier, and the active (tier-scaled) rate limits. If other tools are missing, update the key's scopes in the Viktor dashboard and refresh the client's tool list.

Recommended workflow

For a one-off task, call ask_viktor with a message:

{
  "message": "Analyze this month's revenue and summarize the biggest changes.",
  "speed": "smarter",
  "timeout_seconds": 120,
  "idempotency_key": "monthly-revenue-2026-07"
}

ask_viktor creates a thread, starts a run, waits up to timeout_seconds, and returns the result when it is ready. Results can contain Markdown, structured JSON, and file artifacts.

If the wait ends first, the response includes wait_timed_out: true and the run_id. Call wait_for_run with that ID. Do not call ask_viktor again, because that can start duplicate work unless the same idempotency key is reused.

For long-running or interactive workflows:

  1. Call create_thread to queue work without waiting.
  2. Call wait_for_run or get_run with the returned run ID.
  3. Call send_message on the same thread to continue the conversation with its history.
  4. Use get_file_download_url for any artifact IDs returned in the result.

Tool reference

Tool What it does Required scopes
ask_viktor Start a new thread and wait for its result threads:create, runs:create, runs:read
create_thread Start a new thread and queue a run without waiting threads:create, runs:create
send_message Continue an existing thread and queue a run messages:create, runs:create
wait_for_run Wait for a run to finish and return its result runs:read
get_run Read a run's current status runs:read
get_run_result Fetch a terminal run's result runs:read
cancel_run Request cancellation of an in-flight run runs:create
list_threads List the key's threads, newest first threads:read
get_thread Get a thread's status threads:read
list_messages List user-visible messages in a thread messages:read
list_runs List a thread's runs, newest first runs:read
run_script Run a Python script directly in the sandbox and wait for its result scripts:execute
wait_for_script_run Wait for a script run to finish and return its result scripts:execute
get_script_run Read a script run's status and result scripts:execute
cancel_script_run Cancel a queued script run scripts:execute
list_integrations List connected integrations and their SDK modules integrations:read
list_integration_tools List an integration's SDK tools with schemas and snippets integrations:read
get_file_download_url Exchange an artifact token for a short-lived URL files:read
whoami Identify the key, scopes, and rate limits None

Script runs without an agent

run_script executes one Python script directly in the workspace sandbox — no agent turn. The script runs as the key owner's identity and can call connected integrations through the workspace SDK. Discover what it can call with list_integrations and list_integration_tools; each tool comes with a runnable snippet that run_script accepts as-is. Output files written to the directory in the VIKTOR_OUTPUT_DIR environment variable come back as artifacts for get_file_download_url. If the wait ends before the script finishes, the response includes wait_timed_out: true — continue with wait_for_script_run, not another run_script. See script runs in the Public API guide for limits.

Structured output

ask_viktor, create_thread, and send_message accept the same response_format JSON Schema as the REST API. When supplied, the completed result includes a validated json value. See the Public API guide.

Timeouts, progress, and retries

ask_viktor and wait_for_run perform bounded server-side polling. Clients that send an MCP progress token receive best-effort progress notifications while waiting.

Every tool call is rate limited against the same policy as its REST equivalent, scaled to the workspace's plan tier (higher plans get proportionally higher limits — see the Public API guide). If a wait times out, call wait_for_run again with the same run ID. Use idempotency_key whenever a tool starts a run so reconnects and retries cannot duplicate work.

Errors

Authentication failures are plain HTTP 401 or 403 responses so MCP clients can recognize credential problems. Tool failures are machine-readable JSON strings with an error and message, and may include http_status, scope, or rate-limit details.

Common fixes:


OpenAI & Anthropic Compatible API

URL: https://viktor.com/docs/chat-completions Summary: Drive Viktor agent runs with the OpenAI or Anthropic SDKs you already use — no new client code required.

If your application already talks to OpenAI or Anthropic, you can point it at Viktor and get an AI employee instead of a bare model. The compatible API accepts the standard Chat Completions, Responses, and Messages wire formats and runs a full Viktor agent behind them — with access to your connected tools.

Quickstart

Grant the chat:completions scope to a key in Settings → API Keys, then swap the base URL, API key, and model in your existing code:

from openai import OpenAI

client = OpenAI(
    base_url="https://api.viktor.com/api/compat/v1",
    api_key="zt_live_sk_...",
)

completion = client.chat.completions.create(
    model="viktor",
    messages=[
        {"role": "user", "content": "Summarize yesterday's Stripe payouts."}
    ],
)
print(completion.choices[0].message.content)

The Anthropic SDK works the same way with base_url="https://api.viktor.com/api/compat".

Endpoints

Method Endpoint Protocol
POST /api/compat/v1/chat/completions OpenAI Chat Completions
POST /api/compat/v1/responses OpenAI Responses
POST /api/compat/v1/messages Anthropic Messages
POST /api/compat/v1/messages/count_tokens Anthropic token counting
GET /api/compat/v1/models Model listing (returns viktor)

Streaming is supported on all three chat surfaces.

How it differs from a bare model

When to use which API

Integrations

Viktor connects to 3,200+ tools. Every integration has a detail page at https://viktor.com/integrations/{slug} and a markdown twin at https://viktor.com/integrations/{slug}/md (also served from the canonical URL via Accept: text/markdown). Each page lists the actions Viktor can run in that tool, example delegations, setup, security, and FAQ. Full directory: https://viktor.com/integrations (A-Z index: https://viktor.com/integrations#integrations-index). Machine-readable URL lists: https://viktor.com/sitemap-integrations-tier1.xml.

The 304 integrations with the deepest coverage are listed below; every other supported tool also has a detail page at the URL pattern above:

Trust Summary

Website Credit

Viktor's public marketing website was designed and built by Grafit Agency: https://www.grafit.agency/

Research

Blog - Comparisons

Blog - Best-of Lists

Blog - Use Cases & Guides

Blog - Technical & Trust

Common Questions