Skip to main content
The /v1 API is authenticated with an API key sent as a bearer token:
Responses are JSON with snake_case fields.

Key types

There are two kinds of workspace key, created on the API keys page: Embed a runtime key in products. Keep management keys for CI, scripts and the SDK’s authoring commands. A runtime key calling a configuration endpoint gets a 403 with an explanation.

Expiration and spend limits

Every key can be given an expiration when it is created; an expired key is rejected like a revoked one (401). A runtime key can also carry a spend limit in USD, optionally reset daily, weekly or monthly (UTC). Spend is the sum of the model cost of runs started with that key; runs whose provider reports no usage count as $0. Once the limit is reached, runs return 402 with a message saying when the counter resets. A machine-readable OpenAPI 3.1 spec covers every endpoint with operationIds for function calling. Prefer calling agents from an MCP client? See the MCP server.

Rate limits

Requests are rate limited per API key. When you exceed the limit the API returns 429 Too Many Requests with a Retry-After header (seconds). Back off and retry.

Endpoints

Agents & runs

  • GET /v1/agents, POST /v1/agents, PATCH /v1/agents/{id} ({ name?, description? }), DELETE /v1/agents/{id}
  • GET /v1/agents/{id}/versions (the agent’s current graph), POST /v1/agents/{id}/versions
  • GET /v1/agents/{id}/runs, POST /v1/agents/{id}/runs ({ input, thread_id?, context? }, streams SSE)
  • GET /v1/runs, GET /v1/runs/{id} (?include_model_calls=true adds one llm_call step per model call)
  • GET /v1/runs/{id}/otel: the run as OpenTelemetry trace data (OTLP/JSON, GenAI conventions)
  • GET /v1/runs/{id}/screenshots, GET /v1/runs/{id}/screenshots/{seq} (PNG): what the browser tool saw, one per action
  • GET /v1/tools, POST /v1/tools, PATCH /v1/tools/{id} ({ description?, builtin?, http?, mcp? })
  • GET|POST /v1/browser/profiles, DELETE /v1/browser/profiles/{ref}, POST /v1/browser/profiles/{ref}/clear: saved sign-ins for the browser tool
  • GET|POST /v1/browser/credentials, DELETE /v1/browser/credentials/{ref}: portal logins. A password goes in once and is never returned
context on POST /v1/agents/{id}/runs is a small map of string facts your application already knows and the agent should not ask for, such as a wallet address or an account id:
Every agent node in the run sees it in its system prompt, including subagents the run delegates to. At most 10 keys; keys are 1-40 characters of a-z A-Z 0-9 _ . -; values are strings of at most 200 characters. When a message on thread_id answers a paused run, send the same context again so the continuation keeps it. Create a built-in tool with POST /v1/tools:
Available keys: browser, decide, wait_until, verify_plan, plan_write, ask_human, knowledge_search, memory_search, memory_write, list_skills, use_skill, calculator. The browser key takes its settings in the same block: { "kind": "builtin", "builtin": { "key": "browser", "browser": { "allowedDomains": ["portal.example.com"], "approvals": "consequential" } } }. To add one to an agent, GET its versions to read the current graph, push the tool id onto a node’s toolRefs, and POST it back with publish: true. POST /v1/runs/{runId}/answer with { answer } continues a run that paused to ask a human. It returns 202; the outcome shows up on GET /v1/runs/{runId}. A paused run’s interrupt field carries the question.

Schedules

  • GET /v1/schedules?agent=, POST /v1/schedules
  • GET /v1/schedules/{id}, PATCH /v1/schedules/{id}, DELETE /v1/schedules/{id}
POST takes { agent, name, input, frequency, hour?, minute?, weekday?, timezone?, enabled? }. frequency is hourly, daily or weekly; weekday is 0 (Monday) to 6 (Sunday). PATCH accepts any of those fields, including enabled to pause and resume.

API keys

  • GET /v1/api-keys?usage=true, POST /v1/api-keys, DELETE /v1/api-keys/{id} (revoke)
Management keys only. POST takes { name, kind?, expires_in_days?, spend_limit_usd?, spend_reset_period? } and returns the full key once under key.

Knowledge

  • GET /v1/knowledge, POST /v1/knowledge, PATCH /v1/knowledge/{kb} ({ name?, description? }), DELETE /v1/knowledge/{kb}
  • GET /v1/knowledge/{kb}/documents, POST /v1/knowledge/{kb}/documents, PATCH /v1/knowledge/{kb}/documents/{doc} ({ title }), DELETE /v1/knowledge/{kb}/documents/{doc}
  • POST /v1/knowledge/search

Memory

  • GET /v1/memory, POST /v1/memory, PATCH /v1/memory/{id} ({ content }, re-embedded), DELETE /v1/memory/{id}
  • POST /v1/memory/search

Providers

  • GET /v1/providers, POST /v1/providers, DELETE /v1/providers/{id|label}
Management keys only. POST takes { provider, label, api_key, base_url? } where provider is openrouter, openai, openai_compatible or typesafe. Keys are encrypted on write and never returned.

Deployments (hosted apps)

  • GET /v1/deployments, POST /v1/deployments, GET /v1/deployments/availability?name=
  • GET /v1/deployments/{ref}, PATCH /v1/deployments/{ref}, DELETE /v1/deployments/{ref}
  • GET /v1/deployments/{ref}/users, POST /v1/deployments/{ref}/users/{uid}/block, DELETE /v1/deployments/{ref}/users/{uid}/block
  • GET /v1/deployments/{ref}/analytics?days=30, GET /v1/deployments/{ref}/channels
  • POST /v1/deployments/{ref}/assets (multipart kind=logo|hero, file)
  • GET /v1/deployments/{ref}/domain, PUT /v1/deployments/{ref}/domain, POST /v1/deployments/{ref}/domain/verify, DELETE /v1/deployments/{ref}/domain
Management keys only. {ref} is a deployment id or its subdomain. POST takes { agent, subdomain?, branding?, auth_mode?, status? }; the agent must be published, auth_mode is public or require_login, status is draft, live or disabled. branding is { title?, tagline?, logoUrl?, heroImageUrl?, accentColor?, starterPrompts? } and PATCH replaces it whole.

Skills & subagents

  • GET /v1/skills, POST /v1/skills ({ name, body, description?, enabled? }, upsert by name)
  • GET /v1/skills/{ref}, PATCH /v1/skills/{ref} ({ name?, description?, body?, enabled? }), DELETE /v1/skills/{ref}
  • GET /v1/subagents, POST /v1/subagents ({ agent }, id or name), DELETE /v1/subagents/{ref} (tool id or agent name)
A skill’s name is lowercase letters, digits and dashes. A subagent is a subagent tool row; add its id to a node’s toolRefs and push a version to attach it to a parent.

Channels & integrations

  • GET /v1/channels?agent=|deployment=, POST /v1/channels, PATCH /v1/channels/{id} ({ enabled }), DELETE /v1/channels/{id}
  • GET /v1/integrations, POST /v1/integrations ({ connector, secret?, label? }), DELETE /v1/integrations/{id|label|slug}
POST /v1/channels takes { agent | deployment, kind, slack_channel_id?, signing_secret?, slack_bot_token?, telegram_bot_token?, whatsapp_phone_number_id?, whatsapp_waba_id?, whatsapp_access_token?, whatsapp_app_secret? }. Tokens and secrets are encrypted on write and never returned; a Telegram bot’s webhook is registered for you and its URL comes back as webhook_url. A WhatsApp channel is checked with Meta when it is created and comes back with webhook_url and whatsapp_verify_token to paste into your Meta app’s webhook settings. API-key connectors are probed before they are saved; OAuth connectors connect from the dashboard.

WhatsApp on Cruq AI’s number

  • POST /v1/whatsapp/link-codes ({ agent }) returns { code, expires_at, number, wa_link }. Send link <code> to number within 10 minutes; the agent then runs as the key’s user. Keys without a user get 400.
  • GET /v1/whatsapp/links?agent=, DELETE /v1/whatsapp/links/{id}

Members & voice calls

  • GET /v1/members, POST /v1/members/invites ({ email, role? }), DELETE /v1/members/invites/{id|email}
  • PATCH /v1/members/{user_id|email} ({ role }), DELETE /v1/members/{user_id|email}
  • GET /v1/voice-calls?limit=, GET /v1/voice-calls/{id} (with transcript), GET /v1/voice-calls/{id}/recording (WAV)
Management keys only for members. A key acts as an admin: it cannot invite, promote or remove an owner. An invite returns its link as well as sending the email.

Guardrails & prompts

  • GET /v1/guardrails, POST /v1/guardrails, PATCH /v1/guardrails/{name} ({ enabled }), DELETE /v1/guardrails/{name}
  • GET /v1/prompts, POST /v1/prompts, PATCH /v1/prompts/{name} ({ name?, description?, template?, model? }; a rename keeps the version), DELETE /v1/prompts/{name}

Evals

  • GET /v1/evals/suites, POST /v1/evals/suites
  • GET /v1/evals/suites/{ref}, PATCH /v1/evals/suites/{ref}, DELETE /v1/evals/suites/{ref}
  • GET /v1/evals/suites/{ref}/cases, POST /v1/evals/suites/{ref}/cases
  • GET /v1/evals/suites/{ref}/suggestions (?limit=, ?days=, ?agent=): cases picked from the agent’s recent runs. Nothing is saved
  • PATCH /v1/evals/cases/{id} ({ name?, input?, scorers?, weight? }), DELETE /v1/evals/cases/{id}
  • GET /v1/evals/experiments, POST /v1/evals/experiments
  • GET /v1/evals/experiments/{id}, PATCH /v1/evals/experiments/{id}, DELETE /v1/evals/experiments/{id}
  • GET /v1/evals/experiments/{id}/labels, POST /v1/evals/results/{id}/label ({ score: 1-5, note? }): score judged results yourself; the experiment’s judge_agreement says how far the judge agrees
Suites and agents are addressable by id or name. Suite names are unique per agent rather than per workspace, so a name shared by two agents’ suites returns 409 with the matching ids instead of picking one. GET /v1/evals/experiments/{id} returns the experiment and its leaderboard; add ?results=true for the full case x variant matrix. Aggregates are computed on read, so polling a running experiment returns its partial ranking. DELETE on an experiment cancels it: pending cells are skipped, and cells already mid-run are allowed to finish. See Evals for scorer shapes and worked examples.

Run metering

Every run carries a nested usage object. Fields are nullable, and null means unknown - some providers report no usage block, which is not the same as a run that cost nothing. cost_usd is a string because it is an exact decimal.
Eval runs carry source = "eval" and are excluded from GET /v1/runs by default.