# Billing Source: https://docs.capy.ai/admin/billing ## Subscription Manage your plan at [Settings → Org → Billing](https://capy.ai/settings/org/billing). A plan grants credits each month at a discount; it unlocks no features. Nothing is locked behind a subscription, and an organization can run entirely on purchased balance. The plan row shows the current plan and renewal date instead of a separate Active badge. A scheduled cancellation appears beside the plan name, and an invoice that still needs payment appears as a separate **Invoice awaiting payment** row with its hosted payment link. ### Changing or canceling a plan Click **Choose a plan** before subscribing or **Change plan** on an active self-serve plan. The plan picker compares monthly and annual options and marks the current plan. Every plan change applies immediately and may include a prorated charge. To stop renewal, open **Change plan**, then click **Cancel plan** below the plan options. After confirmation, the current plan remains active until the end of its billing cycle. Enterprise plans show a contact-us message instead of self-serve plan controls. ## Workspace type and members The Workspace section shows two separate facts: * **Workspace type** is a plain Personal value with zero or one active member and Team with more than one * **Members** shows the active-member count and links to member management Every plan includes unlimited members and has no per-seat price. Moving from Personal to Team never creates a member charge. ## Usage monitoring [Settings → Org → Usage](https://capy.ai/settings/org/usage) shows the organization's spend over any window: totals by route, per-member and per-model breakdowns, per-thread costs, and token counts. The billing page shows the live balance and invoices, and every thread shows its own cost rollup in the title bar. ## Adding balance Billing changes are admin actions; members see an ask-an-admin prompt. ### Manual balance purchase 1. Go to [Settings → Org → Billing](https://capy.ai/settings/org/billing) 2. Click **Add balance** 3. Pick a preset (\$20, \$100, \$500) or choose **Other** for a custom amount (minimum \$5) 4. Complete the Stripe checkout Purchases are idempotent per intent: retrying a request that lost its response replays the same charge instead of double-billing you. A finite balance shows how much of the remaining amount comes from the plan grant and from purchased credits. An unlimited enterprise grant shows **Unlimited credit grant** with an infinity symbol instead; it has no meter, numeric balance detail, or **Add balance** action because those figures don't apply. ### Auto-reload Auto-reload adds balance automatically when it drops below a threshold, so a long-running thread doesn't die while you sleep. Two limits apply: a reload that would exceed the monthly cap is skipped, and a declined payment stops it. In either case a run can still hit the balance gate. 1. Go to [Settings → Org → Billing](https://capy.ai/settings/org/billing) and click **Enable** on **Auto-reload** 2. Configure: * **When balance goes below** - the dollar threshold that triggers a reload (minimum \$1) * **Reload to** - the reload target (must be at least the threshold) * **Cap monthly reloads** (optional) - enables a hard ceiling on auto-reload purchases per UTC calendar month; the **Monthly cap** must be at least the reload target The monthly cap starts off and can be enabled when you need one. After auto-reload is active, its action changes to **Manage**, where you can update the values or choose **Turn off**. When the balance crosses the threshold, Capy brings it back up to the target through your saved payment method. The monthly cap is enforced before each attempt - an attempt that would exceed the cap is refused rather than allowed to overshoot - and a failed or payment-required attempt can be retried manually from the billing page. Enabling requires a saved payment method. ### Payment method The **Payment method** row opens the hosted billing portal through its **Manage** action. It becomes available after the organization's first purchase. ## Billing activity Billing activity lists each invoice with its plan, date, status, amount, and hosted **View** action. Paid invoices use a quiet **Paid** status; an open provider invoice appears as **Unpaid** in yellow. Other provider states keep their explicit status label. Invoices may take up to 24 hours to appear. ## Daily OSS allowance Approved open-source organizations get \$50 of free usage per UTC day, drawn before any paid billing. Admins choose what happens when the day's allowance runs out: stop until the daily reset, or continue on the organization balance. The allowance card on the billing page shows the day's usage and reset time. The program is granted by Capy - contact us if your open-source project should be on it. ## Upgrading Higher plans include more monthly usage at bigger discounts. Change plans anytime from the billing page; see [plans](/models-and-pricing#plans). ## Enterprise Contact us for custom billing arrangements, volume discounts, and annual contracts. # Members Source: https://docs.capy.ai/admin/members ## Roles Every organization member has one of three roles: | Role | What they can do | | ---------- | ----------------------------------------------------------------- | | **Owner** | Full control - billing, members, settings, integrations, projects | | **Admin** | Manage members, projects, integrations, and settings | | **Member** | Create and run tasks, view projects they have access to | An organization can have multiple owners. Transferring ownership is granting the owner role to another member and then revoking your own. ## Inviting members 1. Go to [Settings → Members](https://capy.ai/settings/org/members) 2. Click **Invite** 3. Enter one or more email addresses and send Every invitation joins as a member; promote to admin after they accept. Invited users receive an email with a link to join, and any member can invite. Pending invitations appear in the members list; the inviter or an admin can revoke one before it's accepted. Every plan includes unlimited members: inviting someone never creates a seat charge, and no invite is ever blocked over member count. The billing page labels a workspace Personal with zero or one active member and Team with more than one, but a second member still doesn't add a member fee. See [pricing](/models-and-pricing#no-seats). ## Join by domain Admins can allow an email domain so anyone with a verified email on it can join without an invitation. 1. Go to [Settings → Members](https://capy.ai/settings/org/members) and add your domain to the allowed list - adding a domain requires your own verified email to match it, and public email domains are rejected 2. Anyone with a verified email on that domain sees your organization as joinable and joins as a member in one click A domain isn't owned by one organization: several organizations can allow the same domain, and a joiner picks which to join. Removing the domain stops future joins; it doesn't remove existing members. ## Managing members From the members page, admins and owners can: * **Change roles** - promote a member to admin, or demote an admin to member * **Remove members** - remove someone from the organization * **Grant or revoke owner** - owners can make another member an owner, or step down (never below one owner) Leaving an organization isn't self-serve - ask an admin to remove you. An organization always keeps at least one owner. ## Service users A service user is a non-human principal for automations and API integrations: it acts as the explicit identity behind an [API key](/api-reference/authentication) or an [automation's run-as identity](/automations), so unattended work never impersonates a person. Service users are not members: they take no invitations, and they carry their own roles and project access, plus optional subscription grants for seat-billed models. # Security Source: https://docs.capy.ai/admin/security Capy's security model starts from one assumption: the agent running on a machine can be manipulated by whatever it reads (a malicious repo, a poisoned web page, a crafted issue). So the design treats the machine as untrusted, and puts nothing durable on it worth stealing. ## One machine per thread Every thread runs on its own isolated Ubuntu 24.04 VM, and every task subagent that writes gets its own. Machines never share state across organizations or users. Machines sleep when idle: the filesystem is persisted to disk and the VM is freed, so nothing keeps running and nothing keeps billing. Waking resumes from disk: memory, processes, and connections don't survive, but your files do. A machine left asleep for 2 days moves to colder storage (still resumable); at 30 idle days it's deleted for good. ## GitHub credentials never land on the machine No reusable GitHub credential (user token, installation token, or app key) ever sits on a machine, at rest or in memory. The agent still does ordinary `git` and `gh` work; the credentials live elsewhere. The only credential on the machine is a short-lived signed grant that's useless against GitHub: it only authenticates to Capy's git proxy. The proxy verifies the grant, attaches a GitHub installation token on Capy's side per operation, and streams the bytes through. Grants are pushed to the machine by Capy's control plane and die on their TTL: even if one is copied off the machine, the copy expires on the same clock, and an asleep machine's grant just expires at rest. Identity still works the way you configured it. Commit author and PR author follow your GitHub connection whether the agent uses the product's PR flow or plain `gh pr create`. Disconnecting your GitHub account in settings stops its use immediately and revokes the token at GitHub. The proxy does credential isolation and nothing else: it doesn't rewrite commands or gate refs. Branch rules are GitHub branch protection, where they belong. ## Secret values stay sealed Environment variables you configure for a project are customer data, encrypted under a dedicated KMS boundary. Each value is sealed to its exact project, scope, and name, so ciphertext moved anywhere else won't open. Values are decrypted only to write an env file on an authorized machine during setup: mode 0600, outside the repository tree, excluded from diffs and snapshots, never placed in command arguments. The agent sees names and configured status only; Capy itself never surfaces a value in prompts, list APIs, logs, events, or error text. But a value written to the machine is part of the environment there, so a command the agent runs can print it, and that output enters the transcript like any other output. The [secrets page](/secrets) states this threat model plainly. The single place a value can be read back is an authorized human session in the app, one name at a time; there is no bulk value read anywhere. Values you hand to a single running thread through the composer are narrower still: write-only, with no reveal endpoint at all. ## Capy never merges on its own Capy never merges a PR or enables auto-merge unless you explicitly ask for that specific merge in the current conversation. The rule covers every branch and every path: `gh pr merge`, merge API calls, and auto-merge enablement alike. This is one of three rules with no override: no merging without an explicit ask, no exposing or committing secrets, no fabricating results. Your `AGENTS.md` files and project rules can reshape everything else about how Capy works; they can't relax these. ## Data handling Your code is processed only to serve your requests; Capy does not train models on your code. Conversation history and thread state are stored encrypted on Capy's backend, and machine filesystems follow the retention lifecycle above: colder storage after 2 idle days, deletion at 30. For enterprise compliance requirements, [contact us](/support). # Authentication Source: https://docs.capy.ai/api-reference/authentication API keys, the principal they act as, and when to use a service user Every request to the public API authenticates with an API key sent as a bearer token: ```bash theme={null} curl -H "Authorization: Bearer $CAPY_API_KEY" \ "https://api.capy.ai/api/v1/threads?projectId=$PROJECT_ID" ``` ## Keys A key is an opaque credential (`capy_` followed by 43 random characters) scoped to one organization. Keys are minted only in the app under **Settings → API**; there is no API endpoint for minting, renaming, or revoking keys, so possession of a key never lets you make more keys. * The plaintext is shown exactly once at mint. Capy stores only a digest, so a lost key is a revoke plus a new mint, never a recovery. * Keys optionally expire, set at mint only. * There is no rotate verb: rotation is minting a new key and revoking the old one, with the overlap under your control. * A missing, revoked, or expired key all answer the same `capy/Unauthorized`; the failure reason is deliberately not disclosed to the caller. ## Every key acts as a principal A key names one explicit acting principal: a human user or a service user. The key itself grants no authority: it's possession proof plus organization selection, and every request re-resolves the live principal. That "acting as" has two consequences: * **Attribution.** Threads created with the key belong to the principal, usage bills attribute to it, and commits carry its identity: a human principal's runs commit as that person, a service principal's runs commit under the service user's name with Capy's bot address. Two requests can attribute to a member instead: thread creation takes an optional `authorId` and pin/unpin take an optional `userId`, each naming an active member of the key's organization, so a service integration can create a thread that lists under a teammate's threads and pin it to their board. The key still needs its own access to the project, and the kickoff message stays attributed to the key's principal. * **Permissions.** The principal's live state decides what the key can do, on every request. Remove a member from the organization and their keys stop authenticating on the next request; disable a service user and its keys stop the same way. Re-enabling a service user restores its keys without re-minting. There are no per-key scopes today. A human key acts with your live membership role; a service key acts with the service user's role and project access. ## Service users A service user is a non-human principal owned by your organization: no login, no email, just a name, a role, and project access. It's the right principal for anything that outlives a person's involvement: * **Automations**: an automation running as a departed teammate is auto-disabled when they're offboarded; one running as a service user keeps working. * **CI and integrations**: a bot that creates threads from your pipeline shouldn't act as whoever happened to set it up, in attribution or in permissions. Service users carry: * a **role**: `read_only` (read and list only), `read_write` (create and drive threads within its project access), or `admin` (organization-admin operations on surfaces that accept a service principal; it can never own or delete the organization); * **project access**: all organization projects, or an explicit allowlist; * an optional **monthly spend cap**, checked before new billable work starts. Create service users and mint their keys in **Settings → API**. Any member can create a `read_only` or `read_write` service user; creating an `admin` one takes an organization admin or owner. ## Subscription grants for seat-billed models Some models route through a subscription seat (Claude, Codex OAuth routes) rather than Capy's metered billing. A service user can use those only through an explicit grant: a member lends their subscription to a specific service user, and the grant is checked live on every use: revoking it, or the member leaving the organization, cuts the route immediately. The grant is credential custody only, never billing delegation: Capy-billed work by a service user always bills your organization under the service user's own identity. And there is no fallback: if the granted subscription is exhausted or revoked, the run fails on that model rather than silently switching to a different payer. ## Practical guidance Use your own key for exploration and personal scripts, where acting as yourself is the point. The moment something runs unattended (cron, CI, an automation's run-as principal, a shared integration), give it a service user with the narrowest role and project access that works, and set a spend cap. You'll revoke and re-mint human keys as people come and go; the service user's key survives all of it. # Cancel batch Source: https://docs.capy.ai/api-reference/automations/cancel-batch /openapi.json post /api/v1/automations/{automationId}/batches/{batchId}/cancel # Create automation Source: https://docs.capy.ai/api-reference/automations/create-automation /openapi.json post /api/v1/automations # Delete automation Source: https://docs.capy.ai/api-reference/automations/delete-automation /openapi.json post /api/v1/automations/{automationId}/delete # Disable automation Source: https://docs.capy.ai/api-reference/automations/disable-automation /openapi.json post /api/v1/automations/{automationId}/disable # Enable automation Source: https://docs.capy.ai/api-reference/automations/enable-automation /openapi.json post /api/v1/automations/{automationId}/enable # Flush batch Source: https://docs.capy.ai/api-reference/automations/flush-batch /openapi.json post /api/v1/automations/{automationId}/batches/{batchId}/flush # List automations Source: https://docs.capy.ai/api-reference/automations/list-automations /openapi.json get /api/v1/automations # List batches Source: https://docs.capy.ai/api-reference/automations/list-batches /openapi.json get /api/v1/automations/{automationId}/batches # Restore automation Source: https://docs.capy.ai/api-reference/automations/restore-automation /openapi.json post /api/v1/automations/{automationId}/restore # Common flows Source: https://docs.capy.ai/api-reference/common-flows End-to-end curl sequences: drive a thread, walk its task tree, start a review, organize a member's board, trigger an automation Six flows you'll actually build, each complete enough to paste into a script. All of them assume: ```bash theme={null} export CAPY_API_KEY="capy_..." export API="https://api.capy.ai/api/v1" auth() { curl -sS -H "Authorization: Bearer $CAPY_API_KEY" "$@"; } ``` ## Drive a thread end to end Create with a caller-minted `requestId` so a timed-out create can be retried without starting a second run: ```bash theme={null} THREAD_ID=$(auth -X POST "$API/threads" \ -H "Content-Type: application/json" \ -d '{ "requestId": "'"$(uuidgen)"'", "projectId": "'"$PROJECT_ID"'", "message": "Upgrade the CI pipeline to Node 24 and open a PR." }' | jq -r .id) ``` Poll until the thread leaves its working states: ```bash theme={null} while :; do STATUS=$(auth "$API/threads/$THREAD_ID" | jq -r .status) echo "$STATUS" case "$STATUS" in active|waiting) sleep 10 ;; *) break ;; esac done ``` `pending_user` means the agent asked you something; `ready_for_review` means it delivered. Either way, the answer is in the transcript: ```bash theme={null} auth "$API/threads/$THREAD_ID/messages" | jq -r '.items[] | "\(.source): \(.text)"' ``` Assistant entries carry the agent's replies, including the PR link when it opened one. Tool entries are one-line activity summaries, never raw arguments or results. Page with `after` set to the response's `cursor` until it's null. Reply, and the loop starts again: ```bash theme={null} auth -X POST "$API/threads/$THREAD_ID/message" \ -H "Content-Type: application/json" \ -d '{"text": "CI is green but the lockfile changed; explain why."}' ``` A message interrupts the agent's current work by default. Pass `"delivery": "queue"` to let the current work finish first, and the receipt's `id` is what you'd pass to `POST $API/threads/$THREAD_ID/messages/{id}/cancel` if you change your mind while it's still waiting. ## Inspect a thread's task tree Agents fan work out to task subagents, which either share the parent's machine or run on a fresh one. The task endpoints are read-only observation: a task is driven through its thread, so if you want a task to change course, message the thread. List the tree; pages walk depth-first, so parents always precede children and every page prefix is a coherent subtree: ```bash theme={null} auth "$API/threads/$THREAD_ID/tasks" | jq -r '.items[] | "\(.taskPath) \(.status) \(.title)"' ``` `taskPath` is the dotted address from the thread root: `1.2` is the second task under the first task. `status` is the run state: `working`, `waiting`, `idle`, `done`, or `failed`. Each task's `usage` is its own subtree's spend, so you can see where the credits went. Read one task and its transcript with the same `Message` shape threads use: ```bash theme={null} auth "$API/tasks/$TASK_ID" auth "$API/tasks/$TASK_ID/messages" | jq -r '.items[] | "\(.source): \(.text)"' ``` ## Start a review on a pull request `POST /reviews` starts a Capy review round on any PR in a repository your organization's GitHub installation covers: ```bash theme={null} auth -X POST "$API/reviews" \ -H "Content-Type: application/json" \ -d '{ "repo": "acme/checkout", "prNumber": 481, "idempotencyKey": "release-gate-481" }' ``` ```json theme={null} { "reviewId": "rev_...", "requestId": "release-gate-481", "threadId": "jam_01...", "headSha": "b1c2d3...", "adopted": false } ``` A review round is keyed to the PR's exact head and base commits, so retriggering the same code answers the existing round with `adopted: true` and runs nothing new. Push a commit and the next call opens a fresh round. Omit `idempotencyKey` and this scope keying is the whole dedup; pass `forceRefresh: true` for a confirmed manual re-run of an already-completed round. If one of your threads should hear the verdict, pass its id as `sourceThreadId`: the round's outcome lands in that thread as an ordinary input and wakes it, instead of you polling. The response's `sourceRecorded` tells you whether that notification was actually recorded (false when the adopted round already reports elsewhere or already finished; read the findings in the app instead of waiting). A closed or draft PR, or a PR GitHub won't let Capy read, answers `capy/ReviewRefused` (422) with the reason. ## Create, inspect, and pause automations Create an enabled schedule automation and retain its id. Keep the same request id when retrying an ambiguous response: ```bash theme={null} REQUEST_ID=$(uuidgen) AUTOMATION_ID=$(auth -X POST "$API/automations" \ -H "Content-Type: application/json" \ -d '{ "requestId": "'"$REQUEST_ID"'", "projectId": "'"$PROJECT_ID"'", "name": "Daily dependency audit", "prompt": "Audit dependencies for security advisories and open a PR for required upgrades.", "triggers": [{ "type": "schedule", "cron": "0 3 * * *", "timezone": "UTC" }] }' | jq -r .id) ``` If you create an `incoming_webhook` trigger, save the response's `webhookUrl` at this point. The capability URL is returned only by the first successful request; an idempotent replay returns the existing automation with `webhookUrl: null`, and later reads and mutations omit the field. A human key can create automations. A service-user key needs `read_write` or `admin`, and its project allowlist applies to the requested `projectId`; `read_only` receives `403`. List the automations visible to the API-key principal. Pass `projectId` when the integration should inspect one project only: ```bash theme={null} auth "$API/automations?projectId=$PROJECT_ID" \ | jq -r '.items[] | "\(.id) \(if .enabled then "enabled" else "disabled" end) \(.name)"' ``` The response is cursor-paginated. Pass a non-null `cursor` back unchanged to read the next page. Pause the automation that's causing unwanted runs: ```bash theme={null} auth -X POST "$API/automations/$AUTOMATION_ID/disable" \ | jq '{id, name, enabled}' ``` The response's `enabled` field is `false` after the schedules and provider routes have been removed. Resume it with the inverse transition: ```bash theme={null} auth -X POST "$API/automations/$AUTOMATION_ID/enable" \ | jq '{id, name, enabled}' ``` Enabling revalidates the stored run-as principal before rebuilding schedules and provider routes. Human API keys and service-user keys with `read_write` or `admin` can change state; a `read_only` service key can inspect automations but receives `403` for either mutation. Delete and restore use the same authorization and return the resulting `deleted` state: ```bash theme={null} auth -X POST "$API/automations/$AUTOMATION_ID/delete" auth -X POST "$API/automations/$AUTOMATION_ID/restore" ``` ## Create a thread for a teammate and put it on their board A service-user key can attribute work to a member so it shows up as theirs in the app. First resolve the member: `GET /users` lists your organization's active members, and every id it returns is a valid attribution target: ```bash theme={null} MEMBER_USER_ID=$(auth "$API/users" | jq -r '.items[] | select(.email == "jordan@example.com") | .id') ``` Create the thread with `authorId`, then pin it to that member's board with the same id: ```bash theme={null} THREAD_ID=$(auth -X POST "$API/threads" \ -H "Content-Type: application/json" \ -d '{ "requestId": "'"$(uuidgen)"'", "projectId": "'"$PROJECT_ID"'", "message": "Triage the overnight Sentry alerts and summarize what needs a human.", "authorId": "'"$MEMBER_USER_ID"'" }' | jq -r .id) auth -X POST "$API/threads/pin" \ -H "Content-Type: application/json" \ -d '{"threadIds": ["'"$THREAD_ID"'"], "userId": "'"$MEMBER_USER_ID"'"}' ``` Both calls validate the id against your organization's active members and answer `400` `capy/InvalidRequest` for anyone else. The thread now lists under the member's threads, they're seeded as a participant, and the attribution reads back as `Thread.authorId`; the kickoff message stays attributed to your key's principal. A human key can omit `userId` to pin as itself; a service key must pass it, because a service principal has no board of its own. Pinned is per-member and private, so your service key can write to it but not read it back: confirm through the mutation's `outcomes` (`ok` per thread). Shared folders are readable and writable by any key. List what your key can see, file the thread, and page the folder's threads in board order: ```bash theme={null} auth "$API/folders" | jq '.items[] | {id, name, kind, visibility}' auth -X POST "$API/folders/$FOLDER_ID/threads" \ -H "Content-Type: application/json" \ -d '{"threadIds": ["'"$THREAD_ID"'"]}' auth "$API/folders/$FOLDER_ID/threads?limit=50" | jq '{ids: [.items[].id], cursor}' ``` Filing answers one outcome per thread: `ok` (re-filing an already-filed thread is also `ok`), `not_found` (absent, another organization's, or outside a service key's project access, deliberately indistinguishable), or `not_allowed` (a personal thread refused by a shared folder). A `read_only` service key can list and page folders but receives `403` for every mutation. Folders are created and managed in the app; the API files into them. ## Trigger an automation webhook An [automation](/automations) with an incoming-webhook trigger exposes a capability URL. Note this one is **not** under `/api/v1` and takes no bearer header, because the URL itself is the credential: ```bash theme={null} curl -sS -X POST "https://api.capy.ai/webhooks/automations/$WEBHOOK_SECRET" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: incident-7231" \ -d '{"service": "checkout", "alert": "p99 latency 4x baseline", "since": "2026-08-11T14:02:00Z"}' ``` `Idempotency-Key` is your retry safety: deliveries with the same key start one run, so your alerting system can fire the hook at-least-once without fanning out runs. A `202` body of `accepted` means a run was admitted, `duplicate` means your key already ran, and `no-match` means the automation's trigger filters rejected this body: a valid delivery that starts no run, so don't retry it. `404` means the URL is wrong or the automation is disabled, deliberately indistinguishable. The body reaches the agent as quoted, untrusted event context under the automation's stored prompt: send data for the agent to act on, not instructions. # File Source: https://docs.capy.ai/api-reference/folders/file /openapi.json post /api/v1/folders/{folderId}/threads # List Source: https://docs.capy.ai/api-reference/folders/list /openapi.json get /api/v1/folders # Pin Source: https://docs.capy.ai/api-reference/folders/pin /openapi.json post /api/v1/threads/pin # Threads Source: https://docs.capy.ai/api-reference/folders/threads /openapi.json get /api/v1/folders/{folderId}/threads # Unfile Source: https://docs.capy.ai/api-reference/folders/unfile /openapi.json delete /api/v1/folders/{folderId}/threads # Unpin Source: https://docs.capy.ai/api-reference/folders/unpin /openapi.json post /api/v1/threads/unpin # Cancel queued message Source: https://docs.capy.ai/api-reference/messages/cancel-queued-message /openapi.json post /api/v1/threads/{threadId}/messages/{eventId}/cancel # Interrupt thread Source: https://docs.capy.ai/api-reference/messages/interrupt-thread /openapi.json post /api/v1/threads/{threadId}/interrupt # List thread messages Source: https://docs.capy.ai/api-reference/messages/list-thread-messages /openapi.json get /api/v1/threads/{threadId}/messages # Send message Source: https://docs.capy.ai/api-reference/messages/send-message /openapi.json post /api/v1/threads/{threadId}/message # Send queued message now Source: https://docs.capy.ai/api-reference/messages/send-queued-message-now /openapi.json post /api/v1/threads/{threadId}/messages/{eventId}/send-now # Migrate from the legacy API Source: https://docs.capy.ai/api-reference/migration What changed between the legacy Capy API and the current public API, and how to migrate an integration The public API moved and its resource shapes changed. This page walks through the migration for a person; the [agent brief](#agent-brief) at the bottom is the same content as a ready-made task prompt you can paste into any coding agent. ## The fastest path Legacy personal tokens did not migrate. Create an organization-scoped key in [Settings → API](https://capy.ai/settings/api) — as yourself, or as a service user for least-privilege automation. Copy the [agent brief](#agent-brief) below and give it to your coding agent together with access to your integration's repository. The brief requires the agent to report changed files, removed endpoints, unsupported capabilities, and the checks it ran. Review that report before cutting over. ## Base URL and authentication `https://capy.ai/api/v1` is retired; the current base is `https://api.capy.ai/api/v1` (don't append another `/api` or `/v1`). Authentication is unchanged in shape — `Authorization: Bearer capy_...` — but the key itself is new: legacy personal tokens were user-scoped across organizations and never migrated, so create an organization-scoped key in [Settings → API](https://capy.ai/settings/api). Keys have no per-endpoint scopes; for least-privilege automation, create a service user with the role and project access you want and mint the key for it. A safe smoke test for a new key: ```bash theme={null} curl -sS "https://api.capy.ai/api/v1/projects" \ -H "Authorization: Bearer $CAPY_API_TOKEN" ``` Project IDs carried over from the legacy system, and project discovery is public: `GET /projects` lists the projects your key can access and `GET /projects/{projectId}` reads one, so a configured project ID keeps working and new ones resolve through the API instead of the app. ## What changed | Flow | Legacy | Current | | ----------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Discover projects | `GET /projects` with `limit`/offset paging | Same paths, unpaged: `GET /projects` returns every project your key can access as `{ items }`, `GET /projects/{projectId}` reads one. Items carry `id`, `name`, `description`, `code`, `repos` (`repoFullName`, `baseBranch`), and timestamps | | Create thread | `POST /threads` with `{ projectId, prompt }` | `POST /threads` with `{ requestId, projectId, message }` plus optional `title`, `model`, `machineSize`, `authorId` (attribute the thread to an organization member). `requestId` (1–191 chars) is your idempotency key: reusing it on a retry returns the original thread instead of a duplicate billable run | | Poll status | `runState`, `waitingOn`, `blockedOn`, `pendingWakeups` | One `status` field: `active`, `waiting`, `pending_user` (input required), `error`, `ready_for_review`, `idle`, `archived` | | Send message | `POST /threads/{id}/message` with `{ message, mode }` | Same path with `{ text, delivery }` — `interrupt` (default), `queue`, or `steer` — returning a receipt `{ id, deduped }`. No idempotency key: don't blindly retry a timed-out send | | Queued messages | — | `POST .../messages/{eventId}/cancel` dequeues, `POST .../messages/{eventId}/send-now` promotes; `eventId` is the receipt's `id` | | Stop work | `POST /threads/{id}/stop`; archive stopped active work | `POST /threads/{id}/interrupt`; archive no longer stops anything — call both when you want both | | Read transcript | `content` field, `nextCursor` + `hasMore` | `text` field with `source` of `user`/`assistant`/`tool`; pages are `{ items, cursor }` with `after`, `null` cursor = caught up, cursors are opaque | | Tasks | Create/message/start/stop/PR routes | Read-only child threads: `GET /threads/{id}/tasks`, `GET /tasks/{taskId}` (statuses `working`, `waiting`, `idle`, `done`, `failed`); drive work by messaging the root thread | | Usage | Legacy `GET /usage` shape | `GET /usage` returns the current UTC month through now; pass ISO `from`/`to` timestamps for another window. Billed LLM, image, machine, and total dollars; token totals; per-member, per-model, and per-image-endpoint splits. Per-thread three-kind totals stay on `Thread.usage` (one credit = \$0.001) | Creating a thread end to end: ```bash theme={null} curl -sS -X POST "https://api.capy.ai/api/v1/threads" \ -H "Authorization: Bearer $CAPY_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "requestId": "deploy-fix-1", "projectId": "'$CAPY_PROJECT_ID'", "message": "Investigate the failing integration" }' ``` ## Errors The legacy `{ "error": { "code", "message" } }` wrapper is gone. Errors are an HTTP status plus a tagged object — branch on the status first, then `_tag`: ```json theme={null} { "_tag": "capy/ThreadNotFound", "threadId": "jam_..." } ``` Inaccessible and absent resources intentionally share the same `404`, so a thread in another organization is indistinguishable from one that doesn't exist. ## Retired capabilities | Legacy capability | What to do instead | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | Tags, setup, snapshots, browser snapshots, personal environment variables | Manage in the app; no public replacement | | `GET /threads/{threadId}/session-token`, `POST /sessions/verify` | No public replacement | | Task mutation routes (create, message, start, stop, PR) and `GET /tasks/{taskId}/diff` | Drive work through the root thread | | `GET /models` | Omit `model` to use the configured default; pick models in the app | | Thread and message `attachmentUrls` | No public upload/download capability | If your integration depends on one of these, that dependency is a migration blocker to resolve product-side, not something to paper over in code. ## Cutover checklist * No code, tests, or configuration reference `https://capy.ai/api/v1` * Thread creation sends a stable `requestId` and handles the current `Thread` shape * Stop paths call `/interrupt`; pagination trusts `cursor` and treats `null` as done * Error handling branches on HTTP status and `_tag` * The old key is revoked after the new organization-scoped key is live ## Agent brief Copy the migration brief The button copies the full agent brief to your clipboard (or downloads it where clipboard access is unavailable). Paste it into your coding agent as the task prompt.
````markdown theme={null} # Migrate an integration to the current Capy API Use this brief as the task prompt for an agent migrating customer code from the legacy Capy API to the current public API. ## Goal Update the integration so it uses the current Capy API safely, preserves existing project configuration, handles the new resource shapes, and no longer calls retired endpoints. Do not expose API keys, make unapproved production mutations, or claim success without running the integration's relevant checks. ## Authoritative API facts - Current base URL: `https://api.capy.ai/api/v1` - Retired base URL: `https://capy.ai/api/v1` - Authentication: `Authorization: Bearer capy_...` - Create a current organization-scoped key in **Settings → API**. Legacy personal tokens never migrate — they were user-scoped across organizations, and current keys are organization-scoped. Legacy service-user tokens are planned to carry over unchanged, but do not depend on one until it verifies against the current API. - The current OpenAPI document is the one published with this documentation site. Do not generate a client from the legacy OpenAPI document. - Public resources currently cover projects (read-only discovery), threads, messages, read-only tasks, folders (including pinning), automations, reviews, and usage reporting. - Project IDs from the legacy system are preserved, and project discovery is public: `GET /projects` lists the projects the key can access and `GET /projects/{projectId}` reads one. A configured project ID keeps working; project configuration itself stays app-only. Use environment variables rather than literals: ```bash CAPY_API_BASE=https://api.capy.ai/api/v1 CAPY_API_TOKEN=capy_xxxx CAPY_PROJECT_ID=your-project-id ``` Never print `CAPY_API_TOKEN` in logs or test output. ## Required migration process 1. Read the repository's agent instructions and identify every Capy API client, endpoint string, request/response type, retry policy, polling loop, fixture, test, and customer-facing configuration reference. 2. Search for at least: `capy.ai/api`, `/v1/projects`, `/v1/threads`, `/v1/tasks`, `nextCursor`, `hasMore`, `prompt`, `messageId`, `mode`, `runState`, and legacy error codes. 3. Record which retired capabilities the integration uses. If a required capability has no current public replacement, do not silently remove behavior; report it as a blocker with the exact call site. 4. Implement the endpoint, payload, response, pagination, status, and retry changes below. 5. Update relevant fixtures and existing tests. Add tests only when the repository's instructions permit them. 6. Run the smallest typecheck, lint, formatting, and test commands covering the changed code. 7. Report changed behavior, unsupported dependencies, checks run, and any production cutover steps. ## Base URL and authentication Replace: ```text https://capy.ai/api/v1 ``` with: ```text https://api.capy.ai/api/v1 ``` Do not append a second `/api` or `/v1` segment. Current API keys do not have per-endpoint scopes. For least-privilege automation, use a service user with the required role and project access, then create the key for that service user. A safe read-only smoke request is: ```bash curl -sS "$CAPY_API_BASE/projects" \\ -H "Authorization: Bearer $CAPY_API_TOKEN" ``` A `401` response uses this shape: ```json { "_tag": "capy/Unauthorized" } ``` ## Endpoint crosswalk | Legacy API | Current API or required action | | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /projects` | Same path, unpaged: `{ items }` of every project the key can access, with no `limit`/offset parameters. Items carry `id`, `name`, `description`, `code`, `repos` (`repoFullName`, `baseBranch`), `createdAt`, and `updatedAt`. | | `GET /projects/{projectId}` | Same path, same item shape. Inaccessible and deleted projects answer `404` like absent ones. Project configuration (setup, snapshots, environment variables) stays app-only. | | `GET /threads` | Same path. Supported query fields are `projectId`, `status`, `limit`, and `cursor`. | | `POST /threads` | Same path with a new request body; see below. | | `GET /threads/{threadId}` | Same path with the new `Thread` response. | | `POST /threads/{threadId}/message` | Same path with `{ text, model?, delivery? }`. | | `GET /threads/{threadId}/messages` | Same path with `after` pagination and the new `Message` response. | | `POST /threads/{threadId}/stop` | Replace with `POST /threads/{threadId}/interrupt`. | | `POST /threads/{threadId}/archive` | Same path; now returns the complete `Thread`. It does not stop active work. | | `POST /threads/{threadId}/unarchive` | Same path; now returns the complete `Thread`. | | `PUT /threads/{threadId}/tags` | No public replacement. | | `GET /threads/{threadId}/session-token` | No public replacement. | | `GET /tasks/{taskId}` | Same path, but use only the opaque child-task ID returned by the current API. | | Legacy task list/create/message/start/stop/PR routes | No direct replacement. Read tasks through the root thread and drive work through that root thread. | | `GET /tasks/{taskId}/diff` | No public replacement. | | `GET /models` | No public replacement. Omit `model` to use the configured default. | | `GET /projects/{projectId}/automations` | Replace with `GET /automations?projectId={projectId}`. The response is cursor-paginated and includes each automation's standing configuration and enabled state. | | `POST /projects/{projectId}/automations` | Replace with `POST /automations`, move `projectId` into the request body, and add a caller-stable `requestId`. The first successful response includes a one-time `webhookUrl` when the trigger set contains `incoming_webhook`; retries converge on the same automation without redisplaying the credential. | | Legacy automation update or delete routes | General updates have no public replacement. Use `POST /automations/{automationId}/disable` and `/enable` to pause or resume runs, and `/delete` and `/restore` for the soft-delete lifecycle. | | `GET /usage` | Same path with a new response: `GET /usage` returns a `UsageReport` for the current UTC month through now — pass ISO `from`/`to` timestamps to choose another window — with LLM, image, machine, and total dollars; token totals; and per-member, per-model, and per-image-endpoint splits. Per-thread three-kind credit totals remain on `Thread.usage` and `Task.usage`. | | Setup, snapshots, tags, browser snapshots, personal environment variables, session verification | No current public replacement; manage these in the app. | | Thread or message `attachmentUrls` | No current public upload/download capability. | ## Create a thread Replace the legacy `{ projectId, prompt, ... }` request with: ```json { "requestId": "caller-stable-idempotency-key", "projectId": "configured-project-id", "message": "Investigate the failing integration" } ``` Optional fields: ```json { "title": "Custom title", "model": { "modelId": "model-id", "reasoningMode": "high", "modes": { "fast": true } }, "machineSize": "medium", "authorId": "user-id" } ``` Allowed machine sizes are `small`, `medium`, `large`, `ultra`, `hyper`, and `bigguy`. `authorId` attributes the thread to an active member of the key's organization: the thread lists under that member's threads, they are seeded as a participant, and the attribution reads back as `Thread.authorId`, while the kickoff message keeps the key's own principal as its author. The key still needs its own access to the project. An id that is not an active member answers `400` with `{ "_tag": "capy/InvalidRequest" }`, and a retried `requestId` returns the original thread with its original attribution even if the `authorId` changed or the member has since left. `GET /users` lists the organization's active members as `{ id, email, firstName, lastName }`, so resolve a person by email or name to the exact set of ids `authorId` accepts. `requestId` must be a caller-chosen string of 1–191 characters. Reuse it only when retrying the same logical create request. This makes thread creation retry-safe and prevents duplicate billable runs. The response is a `Thread`: ```json { "id": "jam_...", "projectId": "...", "authorId": null, "title": "Investigate the failing integration", "titleCustom": false, "status": "active", "archived": false, "lastModelId": null, "usage": { "llmCredits": 0, "imageCredits": 0, "vmCredits": 0, "totalCredits": 0 }, "createdAt": "2026-08-10T18:00:00.000Z", "updatedAt": "2026-08-10T18:00:00.000Z", "lastActivityAt": "2026-08-10T18:00:00.000Z" } ``` One credit equals $0.001 USD. Thread usage includes the complete task subtree. ## Thread status Handle every current thread status: - `active` - `waiting`: asynchronous work may continue without customer action - `pending_user`: customer input is required - `error` - `ready_for_review` - `idle` - `archived` Do not carry forward logic based on the legacy `runState`, `waitingOn`, `blockedOn`, or `pendingWakeups` fields; they are not present on the current public `Thread`. ## Messages, interruption, and retries Replace the legacy message body: ```json { "message": "Focus here", "mode": "interrupt" } ``` with: ```json { "text": "Focus here", "delivery": "interrupt" } ``` `delivery` is optional and defaults to `interrupt`. Allowed values: - `interrupt` - `queue` - `steer` The response is an admission receipt: ```json { "id": "01K...", "deduped": false } ``` Message sends do not currently accept a caller idempotency key. Do not automatically retry a timed-out message request unless duplicate delivery is acceptable or the integration performs its own reconciliation. Queued and steered messages can be changed before delivery: - `POST /threads/{threadId}/messages/{eventId}/cancel` returns `{ "outcome": "cancelled" }` or `{ "outcome": "tooLate" }`. - `POST /threads/{threadId}/messages/{eventId}/send-now` returns `{ "outcome": "sent", "id": "..." }` or `{ "outcome": "tooLate" }`. Use the admission receipt's `id` as `eventId`. Replace stop calls with: ```text POST /threads/{threadId}/interrupt ``` Archiving no longer interrupts active work. When the old behavior expected both actions, call `/interrupt` and then `/archive` explicitly. ## Transcript migration Read a thread transcript with: ```text GET /threads/{threadId}/messages?after={cursor}&limit=50 ``` Current message fields: ```json { "id": "...", "source": "user", "text": "Message body", "createdAt": "2026-08-10T18:00:00.000Z" } ``` Migrate `content` reads to `text`. `source` is now `user`, `assistant`, or `tool`. Tool messages may include `tool`; assistant messages may include `model` and `attachments`; user messages may include `authorName`. The public transcript contains rendered tool summaries, not raw tool arguments, results, or thinking. Do not depend on raw event payloads. ## Pagination migration Remove assumptions about `nextCursor` and `hasMore`. - Thread lists accept `cursor` and return `{ items, cursor }`. - Thread transcripts, task trees, and task transcripts accept `after` and return `{ items, cursor }`. - A `null` response cursor means the caller is caught up. - Pass non-null cursors back unchanged. Do not parse or construct them. ## Task migration Tasks are read-only child threads. List a thread's task tree: ```text GET /threads/{threadId}/tasks?after={taskId}&limit=100 ``` Read a task or its transcript: ```text GET /tasks/{taskId} GET /tasks/{taskId}/messages?after={cursor}&limit=50 ``` Use the exact opaque task `id` returned by the task tree. Do not use a legacy task number or project-code identifier. Current task fields include `id`, `threadId`, `parentId`, `taskPath`, `projectId`, `title`, `status`, `usage`, and timestamps. Handle task status values `working`, `waiting`, `idle`, `done`, and `failed`. To direct or stop task work, send a message or interrupt to the task's root `threadId`. Do not call task mutation routes. ## New current routes Thread title routes: - `PATCH /threads/{threadId}` with `{ "title": "..." }` - Send `{ "title": null }` to return to automatic titles - `POST /threads/{threadId}/regenerate-title` Usage route: - `GET /usage?from={iso}&to={iso}` returns the key-scoped `UsageReport`: billed dollar totals (`llmDollars`, `imageDollars`, `vmDollars`, `totalDollars`), window LLM token totals, per-member billed spend, per-model spend split into billed and unbilled rows, and billed image spend grouped by exact endpoint. `from` defaults to the current UTC month and `to` defaults to now; the response echoes the effective window queried. Review routes: - `GET` and `PUT /review-settings` - `GET /review-billing-transfer` - `POST /review-billing-transfer/offer` - `POST /review-billing-transfer/accept` - `POST /review-billing-transfer/cancel` - `POST /review-billing-transfer/decline` - `POST /reviews` Do not add review integration code unless the customer already uses reviews or explicitly requests it. ## Error migration Remove dependencies on the legacy wrapper: ```json { "error": { "code": "not_found", "message": "..." } } ``` The current API uses HTTP status plus a tagged object: ```json { "_tag": "capy/ThreadNotFound", "threadId": "jam_..." } ``` Branch on the HTTP status first and `_tag` second. Do not expose whether an inaccessible resource exists in another organization; inaccessible and absent resources intentionally share the same `404` behavior. ## Acceptance criteria - No production code, tests, examples, or configuration defaults call `https://capy.ai/api/v1`. - Project IDs come from configuration or `GET /projects`; nothing depends on the legacy `limit`/offset pagination, because the current list is unpaged. - A thread create supplies a stable `requestId`, uses `message`, and handles the current `Thread` response. - Message sends use `text` and `delivery`; message retries cannot silently duplicate work. - Stop behavior uses `/interrupt`; stop-and-archive behavior invokes both operations explicitly. - Pagination uses the response `cursor`, `after` where required, and `null` as the completion condition. - Transcript handling uses `text` and supports `source: "tool"`. - Task handling uses `/threads/{threadId}/tasks`, opaque task IDs, and root-thread mutations. - Error handling uses HTTP status and `_tag`, not legacy `error.code`. - Any dependency on an unsupported public capability is reported with file and line references. - Relevant formatting, lint, typecheck, and existing tests pass. - No secret value appears in source, logs, fixtures, commits, or the final report. ## Required final report Return: 1. The files changed and the migration made in each. 2. Retired endpoints removed or replaced. 3. Unsupported capabilities that block full parity. 4. Checks run and their exact results. 5. Customer cutover steps, including configuration and key rotation. ````
# API reference Source: https://docs.capy.ai/api-reference/overview Programmatic access to Capy: create threads, drive the agent, observe tasks, manage automations, start reviews, and read usage The Capy API gives programmatic access to the same platform the app uses: create threads, send messages, watch the agent work, and read what it produced. Endpoints evolve additively; nothing you integrate against changes shape. A **thread** is a durable agent conversation on Capy's servers, with a VM attached for execution. Create one with an initial message, then read progress through its status and message transcript. Agents spawn **tasks** (subagents), which you observe read-only. Threads keep running after your request returns: you create work, then read progress. ## Base URL ``` https://api.capy.ai/api/v1 ``` ## Authentication Every request carries an API key as a bearer token: ```bash theme={null} curl -H "Authorization: Bearer $CAPY_API_KEY" \ "https://api.capy.ai/api/v1/threads?projectId=$PROJECT_ID" ``` Keys are organization-scoped and act as an explicit principal: you, or a service user your organization owns. Permissions and attribution follow that principal on every request. Mint keys in the app under **Settings → API**; the API never mints its own credentials. See [Authentication](/api-reference/authentication) for the principal model and when to use a service user. ## Resources | Resource | What you can do | | ----------- | ----------------------------------------------------------------------------------------------------------- | | Projects | List the projects your key can access and read one, to resolve the `projectId` threads and automations take | | Threads | Create (optionally attributed to a member via `authorId`), list, get, rename, archive/unarchive | | Messages | Read the transcript, send, interrupt, cancel or expedite a queued message | | Tasks | Observe a thread's task tree and each task's transcript, read-only | | Folders | List visible folders, page a folder's threads, file/unfile threads, pin/unpin (optionally for a member) | | Automations | Create, inspect standing configuration, list by project, disable/enable, delete/restore | | Reviews | Start a code review on a pull request, configure per-repo review settings | | Usage | Read your organization's windowed spend and token totals | | Users | List your organization's active members to resolve a person to the id `authorId` and pin/unpin accept | Tasks are deliberately read-only: a task is driven through its thread. Send the thread a message and its agent redirects the work. ## Pagination Paged lists return `{ items, cursor }`. A null `cursor` means you've reached the end; otherwise pass it back to get the next page. Bounded lists — projects, folders, and users — return everything as `{ items }` with no cursor. Two parameter names: * **Threads and automations** take `cursor`, a transparent `:` keyset token. The timestamp freezes the row's list position at mint time; treat it as opaque and pass it back verbatim. * **Tasks and messages** take `after`, the last item's id, since those orders never move. * **A folder's threads** take `after` with the response's cursor token, a `:` keyset over the folder's own order; pass it back verbatim. ## Errors Errors are tagged JSON objects: match on `_tag`, not on prose. The `message` fields are for humans and may change; the tags and status codes are the contract. ```json theme={null} { "_tag": "capy/ThreadNotFound", "threadId": "jam_01..." } ``` Two conventions: * **404 is the only existence answer.** A resource outside your organization's reach answers exactly like one that doesn't exist, so you can't probe for other tenants' ids. * **401 is deliberately uniform.** A missing, revoked, or expired key all answer the same `capy/Unauthorized`. ## Idempotency Thread creation takes a caller-minted `requestId`: retries of the same request converge on the same thread instead of starting a second billable run. Mint one id per logical create and reuse it across retries. ## The full reference The generated endpoint pages in this section document every route, parameter, and schema, straight from the API contract, so they cannot drift from the server. The complete contract is also available as [OpenAPI JSON](/openapi.json). If you're coming from the Scout `/api/v1`, the [migration guide](/api-reference/migration) maps every old route to its replacement. Inspect the complete machine-readable contract for client generation and validation. Start with the [quickstart](/api-reference/quickstart) to go from a key to a working thread in five curl commands, or [common flows](/api-reference/common-flows) for end-to-end sequences. # Delete environment variables Source: https://docs.capy.ai/api-reference/projects/delete-environment-variables /openapi.json delete /api/v1/projects/{projectId}/environment-variables # Get project Source: https://docs.capy.ai/api-reference/projects/get-project /openapi.json get /api/v1/projects/{projectId} # List environment variables Source: https://docs.capy.ai/api-reference/projects/list-environment-variables /openapi.json get /api/v1/projects/{projectId}/environment-variables # List projects Source: https://docs.capy.ai/api-reference/projects/list-projects /openapi.json get /api/v1/projects # Set environment variables Source: https://docs.capy.ai/api-reference/projects/set-environment-variables /openapi.json put /api/v1/projects/{projectId}/environment-variables # API quickstart Source: https://docs.capy.ai/api-reference/quickstart From an API key to a running agent thread in five curl commands This page takes you from nothing to a thread you can watch working at capy.ai. Everything is copy-paste curl; the only prerequisites are a Capy account with a project set up and a terminal. ## 1. Mint an API key In the app, go to **Settings → API** and create a key. The plaintext is shown exactly once (there's no re-display, only revoke-and-remint), so export it now: ```bash theme={null} export CAPY_API_KEY="capy_..." ``` ## 2. Find your project id Threads live in [projects](/projects). List the projects your key can access and pick one — this also verifies the key: ```bash theme={null} curl -H "Authorization: Bearer $CAPY_API_KEY" \ "https://api.capy.ai/api/v1/projects" ``` ```bash theme={null} export PROJECT_ID="project_..." ``` Newly created projects use `project_...` ids; imported ones keep their original id. A `capy/Unauthorized` here means the key is wrong or revoked; later calls answer `capy/ProjectNotFound` when a project id is wrong or the project isn't reachable from this key's organization. ## 3. Create a thread `requestId` is an idempotency token you mint: retries with the same id return the same thread instead of starting a second billable run. ```bash theme={null} curl -X POST "https://api.capy.ai/api/v1/threads" \ -H "Authorization: Bearer $CAPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requestId": "'"$(uuidgen)"'", "projectId": "'"$PROJECT_ID"'", "message": "Fix the flaky retry test in packages/queue and open a PR." }' ``` The response is the `Thread` projection, the same shape every thread read returns: ```json theme={null} { "id": "jam_01...", "projectId": "proj_...", "title": null, "status": "active", "archived": false, "usage": { "llmCredits": 0, "imageCredits": 0, "vmCredits": 0, "totalCredits": 0 }, ... } ``` Save the id: ```bash theme={null} export THREAD_ID="jam_01..." ``` ## 4. Poll the status ```bash theme={null} curl -H "Authorization: Bearer $CAPY_API_KEY" \ "https://api.capy.ai/api/v1/threads/$THREAD_ID" ``` `status` is what you poll on: | Status | Meaning | | ------------------ | ------------------------------------------------------ | | `active` | The agent is working | | `waiting` | Waiting on something external: CI, a timer, a callback | | `pending_user` | Blocked on your input; read the messages and reply | | `ready_for_review` | Work delivered, awaiting your review | | `idle` | At rest | | `error` | The run failed | | `archived` | Archived | Poll every few seconds until the status leaves `active` and `waiting`. Threads run for minutes, not seconds; a real coding task takes as long as the work does. ## 5. Read the messages ```bash theme={null} curl -H "Authorization: Bearer $CAPY_API_KEY" \ "https://api.capy.ai/api/v1/threads/$THREAD_ID/messages" ``` The transcript is `{ items, cursor }` of `Message` entries: your messages (`source: "user"`), the agent's replies (`source: "assistant"`), and one-line tool activity (`source: "tool"` with the tool name, never raw arguments or results). When the agent opens a pull request, the PR link arrives in its assistant reply text. Page forward by passing the response's `cursor` back as `after`; poll the same way to tail a live thread. ## 6. Reply ```bash theme={null} curl -X POST "https://api.capy.ai/api/v1/threads/$THREAD_ID/message" \ -H "Authorization: Bearer $CAPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Also update the changelog."}' ``` The response is an admit receipt, `{ "id": "...", "deduped": false }`: the message landed and the agent wakes. By default a message interrupts what the agent is doing; pass `"delivery": "queue"` to wait for the current work to finish first. ## You're done Open [capy.ai](https://capy.ai): the thread you just created is on the Threads page, transcript and all, exactly as the API reported it. Everything the app shows rides the same platform you just drove with curl. Next: [common flows](/api-reference/common-flows) for the task tree, reviews, and automation webhooks. # Create review rule Source: https://docs.capy.ai/api-reference/reviewrules/create-review-rule /openapi.json post /api/v1/review-rules # Delete review rule Source: https://docs.capy.ai/api-reference/reviewrules/delete-review-rule /openapi.json delete /api/v1/review-rules/{id} # Get review rule Source: https://docs.capy.ai/api-reference/reviewrules/get-review-rule /openapi.json get /api/v1/review-rules/{id} # List review rules Source: https://docs.capy.ai/api-reference/reviewrules/list-review-rules /openapi.json get /api/v1/review-rules # Update review rule Source: https://docs.capy.ai/api-reference/reviewrules/update-review-rule /openapi.json patch /api/v1/review-rules/{id} # Accept review billing transfer Source: https://docs.capy.ai/api-reference/reviews/accept-review-billing-transfer /openapi.json post /api/v1/review-billing-transfer/accept # Cancel review billing transfer Source: https://docs.capy.ai/api-reference/reviews/cancel-review-billing-transfer /openapi.json post /api/v1/review-billing-transfer/cancel # Configure review settings Source: https://docs.capy.ai/api-reference/reviews/configure-review-settings /openapi.json put /api/v1/review-settings # Decline review billing transfer Source: https://docs.capy.ai/api-reference/reviews/decline-review-billing-transfer /openapi.json post /api/v1/review-billing-transfer/decline # Get review billing transfer Source: https://docs.capy.ai/api-reference/reviews/get-review-billing-transfer /openapi.json get /api/v1/review-billing-transfer # Get review settings Source: https://docs.capy.ai/api-reference/reviews/get-review-settings /openapi.json get /api/v1/review-settings # Offer review billing transfer Source: https://docs.capy.ai/api-reference/reviews/offer-review-billing-transfer /openapi.json post /api/v1/review-billing-transfer/offer # Round Source: https://docs.capy.ai/api-reference/reviews/round /openapi.json get /api/v1/reviews/rounds/{requestId} # Start review Source: https://docs.capy.ai/api-reference/reviews/start-review /openapi.json post /api/v1/reviews # Get task Source: https://docs.capy.ai/api-reference/tasks/get-task /openapi.json get /api/v1/tasks/{taskId} # List task messages Source: https://docs.capy.ai/api-reference/tasks/list-task-messages /openapi.json get /api/v1/tasks/{taskId}/messages # List tasks Source: https://docs.capy.ai/api-reference/tasks/list-tasks /openapi.json get /api/v1/threads/{threadId}/tasks # Archive thread Source: https://docs.capy.ai/api-reference/threads/archive-thread /openapi.json post /api/v1/threads/{threadId}/archive # Create thread Source: https://docs.capy.ai/api-reference/threads/create-thread /openapi.json post /api/v1/threads # Get thread Source: https://docs.capy.ai/api-reference/threads/get-thread /openapi.json get /api/v1/threads/{threadId} # Join Source: https://docs.capy.ai/api-reference/threads/join /openapi.json post /api/v1/threads/{threadId}/join # Leave Source: https://docs.capy.ai/api-reference/threads/leave /openapi.json post /api/v1/threads/{threadId}/leave # List threads Source: https://docs.capy.ai/api-reference/threads/list-threads /openapi.json get /api/v1/threads # Read Source: https://docs.capy.ai/api-reference/threads/read /openapi.json put /api/v1/threads/{threadId}/read # Regenerate thread title Source: https://docs.capy.ai/api-reference/threads/regenerate-thread-title /openapi.json post /api/v1/threads/{threadId}/regenerate-title # Rename thread Source: https://docs.capy.ai/api-reference/threads/rename-thread /openapi.json patch /api/v1/threads/{threadId} # Take over thread Source: https://docs.capy.ai/api-reference/threads/take-over-thread /openapi.json post /api/v1/threads/{threadId}/takeover # Unarchive thread Source: https://docs.capy.ai/api-reference/threads/unarchive-thread /openapi.json post /api/v1/threads/{threadId}/unarchive # Unread Source: https://docs.capy.ai/api-reference/threads/unread /openapi.json put /api/v1/threads/{threadId}/unread # Get usage report Source: https://docs.capy.ai/api-reference/usage/get-usage-report /openapi.json get /api/v1/usage # List organization users Source: https://docs.capy.ai/api-reference/users/list-organization-users /openapi.json get /api/v1/users # Automations Source: https://docs.capy.ai/automations An automation is a stored prompt that belongs to one project and runs when any of its triggers fires. Each run is a normal Capy thread: the agent gets your prompt, the triggering event as context, and a machine to work on. You write the prompt once; the trigger decides when it runs. For examples of recurring and event-driven workflows, see [what you can automate with Capy](https://capy.ai/features/automations). Create automations on the **Automations** page in the app, or ask Capy to create and manage them for you in a thread; the agent has the full set of automation tools. ## Manage automations through the API The public API lets an integration create and inspect automations, then pause, resume, delete, or restore them. Edit and manual-run controls remain in the app and agent tools. Create an automation with the same trigger and run configuration used by the app. It starts enabled unless you send `"enabled": false`; `requestId` is the caller-stable idempotency key, so retries converge on the same automation: ```bash theme={null} REQUEST_ID=$(uuidgen) curl -sS -X POST "https://api.capy.ai/api/v1/automations" \ -H "Authorization: Bearer $CAPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requestId": "'"$REQUEST_ID"'", "projectId": "'"$PROJECT_ID"'", "name": "Daily dependency audit", "prompt": "Audit dependencies for security advisories and open a PR for required upgrades.", "triggers": [{ "type": "schedule", "cron": "0 3 * * *", "timezone": "UTC" }], "threadMode": "new" }' ``` The create response is the stored automation. When its triggers include `incoming_webhook`, the first successful response also carries `webhookUrl`; save it immediately because retries and later reads never return that credential. If that response is lost, delete the original by its returned or listed id and create a replacement with a new `requestId`. List every automation your API-key principal can access, or narrow the page to one project: ```bash theme={null} curl -sS "https://api.capy.ai/api/v1/automations?projectId=$PROJECT_ID" \ -H "Authorization: Bearer $CAPY_API_KEY" ``` The cursor-paginated response includes each automation's prompt, triggers, run configuration, run-as identity, enabled state, run count, and last-triggered time. It never includes incoming-webhook secrets or stored file references. Pause and resume by id: ```bash theme={null} curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/disable" \ -H "Authorization: Bearer $CAPY_API_KEY" curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/enable" \ -H "Authorization: Bearer $CAPY_API_KEY" ``` Delete an automation when it should no longer appear in lists, or restore it later by the id retained from an earlier response: ```bash theme={null} curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/delete" \ -H "Authorization: Bearer $CAPY_API_KEY" curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/restore" \ -H "Authorization: Bearer $CAPY_API_KEY" ``` Every mutation returns the updated automation, including its `deleted` state. A service-user key with `read_only` access can only list; creating or changing state requires `read_write` or `admin`. Project allowlists apply to every automation operation, and an inaccessible id returns the same `404` as an unknown id. See [Common flows](/api-reference/common-flows#create-inspect-and-pause-automations) for a script-friendly sequence and the generated API reference for complete request and response schemas. ## Triggers An automation has 1 to 20 triggers, OR'd together: any one of them firing starts at most one run per event. At most one each of schedule, incoming webhook, and on-demand. | Trigger | Fires on | | ---------------- | ----------------------------------------------------------------------------------------- | | Schedule | A cron expression in an IANA timezone | | GitHub | PR opened/pushed/merged, comments, reviews, labels, checks, branch pushes, `workflow_run` | | Slack | Messages in a channel (grouped into bursts) | | Incoming webhook | An HTTP POST to a per-automation URL | | On demand | A manual run: a button, an API call, or the agent | Each trigger can also carry additional conditions: repositories, branches, authors, and labels for GitHub; channels, authors, and text filters for Slack; substring and regex filters for webhooks. A condition matches when the event matches any of its listed values. ### Schedules Schedules are five-field cron and a IANA timezone, validated at save. The floor is one run per five minutes. DST follows wall-clock semantics in your timezone: a local time that doesn't exist (spring forward) fires once at the next valid instant, and a local time that happens twice (fall back) fires once. If the scheduler falls behind, missed occurrences coalesce into a single run rather than a backlog. ### Slack bursts A burst of Slack messages becomes a single run. Each matched message extends a sliding window (1–300 seconds, default 10); the group closes when the window elapses, a different author posts, or the burst hits its bounds (20 messages, 100 KB). The run gets the joined messages in order. The agent's normal reply channel writes to the run's thread. If you want the automation to post in Slack, say so in the prompt and Capy will use an explicit Slack message tool. ## The run-when gate Trigger conditions are exact filters. For fuzzy conditions like "only when the message is a bug report", add a `run_when` sentence to an event trigger. A fast model checks each matching event against it before the run starts. You are not billed for the fast model check. ## The model `model` is optional at creation, and the choice decides how the automation behaves over time: * **Pinned**: pass `model` and every run uses exactly that model. A pin never silently substitutes: if the run-as principal loses the right to it (the model retires, a subscription grant is revoked), the run fails visibly instead. * **Unpinned**: omit `model` and each run resolves the run-as principal's default model at run creation — their personal default first, then the organization default (Settings → Models). An unpinned automation follows those defaults as they change. The web editor always saves a pinned model: the picker shows the resolved default, and saving pins what it shows. ## Thread mode `thread_mode` decides where runs land: * `new`: every run starts a fresh thread. The default, and right for independent jobs like a nightly check. * `single`: the automation owns one standing thread, and every run delivers its event into it. The thread keeps its machine and its context across runs, so it's right for accumulating work like triaging a channel. ## Who the automation runs as Every automation stores one explicit run-as principal, and every run executes as it: the person who saved the automation, or a service user an admin selected. The triggering actor (the Slack author, the GitHub sender) is never the principal; they appear only inside the event context. * Eligibility is re-verified on every run: live user, live membership, live service-user state, project access. * If the principal is offboarded (user removed, service user disabled), the run fails naming the check that failed, and the automation is disabled with a visible reason. Re-enabling requires picking an eligible principal. * Commits attribute to the principal: a human's runs commit as their interactive threads do; a service user's runs commit under its display name with Capy's bot address. * Model rights are the principal's: a pinned model the principal can't use fails at save, and rights lost later fail the run visibly, never a silent substitution. Default to a service user for anything long-lived. Service users survive offboarding; automations running as a departed teammate stop. Any project member can run any automation in the project, enabled or disabled. Triggering executes the stored prompt as written; it changes nothing. ## The webhook trigger An incoming-webhook automation gets a URL of the form: ``` POST https://api.capy.ai/webhooks/automations/{secret} ``` The URL itself is the credential: there is no signature scheme and no `Authorization` header. Treat it like a password: it's generated server-side, shown once at creation or rotation, and stored only as a digest. Rotation is an immediate cutover; the old URL stops working in the same instant the new one is shown. ```bash theme={null} curl -X POST "https://api.capy.ai/webhooks/automations/$WEBHOOK_SECRET" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: deploy-4821" \ -d '{"service": "checkout", "environment": "staging", "status": "failed"}' ``` * `Idempotency-Key` deduplicates: retries with the same key start one run. Without it, every delivery is distinct. * Bodies are capped at 256 KB (413 above that), and the first 8,000 characters reach the agent as context. * Responses: `202` with `accepted`, `duplicate`, or `no-match` in the body (a body your trigger's filters reject is a valid delivery that starts no run; don't retry it); `404` for an unknown or disabled automation, deliberately indistinguishable; `500` means retry. The body is untrusted event context, never instructions. The agent reads it as data under the standing prompt; a payload that says "ignore your instructions" doesn't. ## Limits `max_runs_per_day` is an opt-in daily cap counting runs admitted across all triggers. An over-cap event records a `skipped` run naming the cap, so throttling is visible in run history, never silent. Unset means no cap. ## Examples * **Nightly dependency check.** Schedule `0 3 * * *`, prompt: audit dependencies for security advisories and open a PR bumping anything vulnerable. * **Follow-up on every merged PR.** GitHub `pull_request_merged`, prompt: check the merged change for docs that now lie and fix them. * **CI failure fixer.** GitHub `workflow_run` with conclusion `failure` on your main branch, prompt: read the failing job, fix the cause, open a PR. * **Slack support triage.** Slack messages in `#support` with a `run_when` of "the message reports a bug", `single` thread mode, prompt: reproduce, file the issue, link it back. * **Deploy-failure investigation.** Incoming webhook from your deploy pipeline, prompt: read the payload, find the regressing commit, report with evidence. # Install on Linux Source: https://docs.capy.ai/desktop-linux The Capy desktop app ships for Linux as an AppImage, a `.deb` package, and a portable `.tar.gz`, each for x64 and arm64. Pick by distribution, not by preference: the AppImage runs anywhere, the `.deb` is only for Debian and Ubuntu, and the tarball is for package maintainers. Every build is listed on the [download page](https://capy.ai/download). ## Which download to pick | Distribution | Download | Updates come from | | --------------------------------------- | ---------------------------------------- | ------------------------------------------------------------- | | Arch, Manjaro, EndeavourOS | `capy-bin` from the AUR, or the AppImage | `pacman` for the AUR package; the app itself for the AppImage | | Fedora, openSUSE, NixOS, anything else | AppImage | The app itself | | Debian, Ubuntu, Linux Mint, Pop!\_OS | `.deb` | The app itself, through `dpkg` | | Packaging Capy for another distribution | `.tar.gz` | Your package | Installing the `.deb` on a distribution without `dpkg` and `apt` does not work and can leave the package half-installed. If that happened, `sudo dpkg --remove capy` cleans it up, then use the AppImage. ## AppImage Download the AppImage for your architecture, mark it executable, and run it: ```bash theme={null} chmod +x capy-*.AppImage ./capy-*.AppImage ``` The AppImage needs no FUSE and no extra libraries. The app updates itself: it checks for a new version a few seconds after launch and every few minutes after that, downloads it in the background, and shows an **Update** control in the tab band when the new version is ready to install. Keep the file where you first ran it, because the update replaces that file in place. ## Arch Linux The [`capy-bin`](https://aur.archlinux.org/packages/capy-bin) package installs the stable release under `/opt/capy` with a `capy` launcher and a desktop entry: ```bash theme={null} yay -S capy-bin ``` Updates arrive through `pacman` like any other package, and the in-app updater stays inactive so the two never fight over the install. Any AUR helper works in place of `yay`. ## Debian and Ubuntu Install the `.deb` with `apt` so its dependencies resolve: ```bash theme={null} sudo apt install ./capy-*.deb ``` The app updates itself. When an update is ready and you choose **Update**, the app asks for your password once to install the new package. ## Portable tarball The `.tar.gz` unpacks to a directory containing the `capy` executable and its resources, with nothing else assumed about the system. It is the input for downstream packages such as `capy-bin`, and it never self-updates: whoever unpacked it delivers the next version, and **Check for Updates** says so. The same applies to an AppImage you extracted with `--appimage-extract`; run the AppImage file itself to get updates. ## Signing in and the keyring The app keeps your session encrypted with the desktop keyring (GNOME Keyring or KWallet) when one is running. On a desktop without a keyring it still signs you in and stores the session under a key built into the app, and the sign-in screen says so. ## If an update fails Choose **Check for Updates** from the Help menu to see why. A full disk is reported as *Not enough disk space to update* with the size the update needs; free that much space and check again. Any other failure means the update feed could not be reached, so check your connection and try again. # Environment Source: https://docs.capy.ai/environment The environment is your project's machine recipe: which setup scripts prepare a fresh [machine](/machines), which processes start on every boot, which commands you and the agent can launch, and how big the VM is. It's the highest-leverage configuration in Capy, because an agent that can't run your tests can't verify its work. On a machine where `pnpm test` works, the agent proves its changes; on a machine where nothing runs, it guesses and you find out in CI. Spend the twenty minutes. Every thread, task, and rebuilt machine in the project benefits, and a [snapshot](#snapshots) makes the result nearly free to boot. ## Two ways to configure it Open your project and go to **Dev environment** to edit machine size, per-repository scripts, startup entries, commands, and snapshots directly, or commit [`.capy/setup.json`](#keeping-setup-in-the-repository) to the repository. Or ask Capy in any thread ("set up this project's dev environment") and it reads your manifests, writes the setup, and can verify it by running the scripts on its own machine. Agent edits and your edits land in the same configuration; every save creates a new version in one shared history. Setup is stored per repository. The shape, if you want to picture it: ```json theme={null} { "vm_size": "large", "repositories": [ { "repository": "acme/app", "scripts": { "initialize": "pnpm install --frozen-lockfile", "refresh": "pnpm install --frozen-lockfile" }, "timeouts": { "initialize": 900 }, "startup": [ { "name": "db", "command": "docker compose up -d --wait postgres" }, { "name": "web", "command": "pnpm dev", "port": 3000 } ], "commands": [ { "name": "test", "command": "pnpm test", "icon": "task" }, { "name": "lint", "command": "pnpm lint", "icon": "search" } ] } ] } ``` Scripts and lists are optional; leave one empty when it has no work to do. ## The two setup phases | Phase | When it runs | Default timeout | | ------------ | --------------------------------------------------------- | --------------- | | `initialize` | Once, on a fresh clone (new machines and snapshot builds) | 900 s | | `refresh` | When a snapshot-restored checkout moves to a newer commit | 300 s | A phase timeout is configurable from 60 to 3600 seconds; a startup entry's from 1 to 3600. The selection is per repository: a fresh clone runs `initialize`; a restored checkout fetches and fast-forwards its branch and runs `refresh` only when HEAD actually moved. Every machine then launches the [startup entries](#startup). A failed `initialize` is retried on the existing checkout until a run succeeds. ### initialize Everything needed to make a clean clone usable: install dependencies, system packages, global tools, generated artifacts. It also runs during snapshot builds, so whatever it produces gets baked into the snapshot. If dependency installation needs a private registry, authenticate explicitly at the top of the script; a package-manager `preinstall` hook can't authenticate the fetch that happens before it exists. ### refresh Bring a prepared machine up to date after its checkout moves. Keep it fast and safe to rerun: sync dependencies from the lockfile, regenerate derived code. Don't duplicate an expensive bootstrap here unless a moved checkout genuinely needs it. ## Startup Startup entries are the processes and preparation a snapshot can't capture: databases, queues, dev servers, secrets written at boot. Each entry is a name and a command, and each runs on every boot in its own terminal session titled with its name. Open the session from the thread to read its logs, or ask Capy to. Entries launch in order. The next entry starts once the previous one is ready, so put infrastructure first and the app after it: * An entry that exits with status 0 is ready. `docker compose up -d --wait postgres` is this kind. * An entry with a `port` is ready once that port is listening, and keeps running in its session. `pnpm dev` with `"port": 3000` is this kind. The port is also published as a preview you can open from the thread. * An entry that exits with a non-zero status fails setup, the same as a failed phase. * An entry that is neither exited nor listening when its timeout (300 s by default) elapses is recorded as not ready and left running; boot continues. Don't background a server with `nohup` and poll for it; declare its port instead. Don't install, build, or migrate in a startup entry; those belong in `initialize` or in a [command](#commands). ## How scripts execute Each script runs from its repository's directory, in bash, under its timeout, as a user with passwordless sudo, with your [environment variables](/secrets) loaded. A non-zero exit or a timeout fails the phase. A failed phase doesn't kill the thread: the machine comes up, the thread gets a workspace warning naming the repository and phase, and the agent continues in a degraded workspace it will usually try to repair by hand. That self-repair costs you time and tokens on every machine; fix the script instead. ## Commands Commands are named actions that run on demand: `test`, `typecheck`, `build`, a database reset, a dev server you don't want on every boot. They never run automatically. You launch one from the machine's menu in the thread pane, and Capy launches one by name; either way it runs in a terminal session titled with its name that you can open. Give a server command its `port` so its preview is published when it's up. Keep commands executable and specific. Prose guidance like "always lint before committing" belongs in [instructions](/instructions), not here. ## Launching from the thread Open the **+** menu in a thread's pane. Under each machine you'll find its startup entries under **Services**, each with a dot for its state (up on its port, running, done, not ready), then a **Commands** row that opens its commands, then its terminals. With one machine those rows sit directly in the menu instead of under a Machines submenu. Click a startup entry to open its terminal session and read its logs, or to relaunch it when it has exited; pick a command to launch it in a new session and open the tab. An entry with a port also appears under Exposed apps on the Overview, where you can open it in the browser. ## What the agent can do itself Capy reads and replaces the whole setup through its environment tools, which is what makes "ask Capy to update your dev environment" work. Two boundaries hold regardless: the agent sees environment variable names and whether each has a value, never the values, and no tool can set a variable's value. Values are yours to manage in the app; see [secrets](/secrets). Every thread's prompt lists the project's startup entries and commands by name, so the agent runs `test` or restarts `api` by name instead of guessing the command; the launch runs in the same titled session you'd open from the menu. After a machine boots, the agent gets one note saying which startup entries came up, which are listening, and which never became ready. PR reviews run on their own machine with a fresh checkout pinned to the exact PR head, credentials read-only and pushes disabled. Shared project variables apply there; personal and thread variables never do. ## Keeping Setup in the repository If you'd rather review setup changes as pull requests, commit `.capy/setup.json` to the repository. The file is that repository's entry, without the repository name and branch: ```json theme={null} { "$schema": "https://api.capy.ai/schemas/setup.v1.json", "format_version": 1, "scripts": { "initialize": "pnpm install --frozen-lockfile", "refresh": "pnpm install --frozen-lockfile" }, "timeouts": { "initialize": 900 }, "startup": [ { "name": "db", "command": "docker compose up -d --wait postgres" }, { "name": "web", "command": "pnpm dev", "port": 3000 } ], "commands": [{ "name": "test", "command": "pnpm test", "icon": "task" }] } ``` Capy reads the file from GitHub on the branch your setup names for that repository, never from a machine's checkout. A push to that branch that changes the file saves a new setup version, attributed to the repository and the commit, and the version history, rollback, and snapshot rebuilds treat it like any other save. In the app the repository shows as owned by its file: its scripts and entries are read-only there, and the agent's setup tool refuses to change them and points at the file instead, so the agent edits `.capy/setup.json` in its checkout and opens a pull request like any other change. A change on a branch does nothing until it merges. To try it first, ask Capy for a test build from that branch; the build reads the branch's file and never activates. Remove the file and the last version it produced stays as an ordinary editable entry. Machine size and tool hooks stay in the app; the file never carries them, and a project can mix repositories with and without a file. ## Snapshots A snapshot is your project's post-setup filesystem (repositories cloned, dependencies installed, `initialize` already run) baked into a bootable image. A fresh machine that restores from one skips the clone and setup work entirely and starts where your setup scripts finished. Restoring from a snapshot takes roughly 1–2 seconds. Without one, every fresh machine pays your full setup time: clone plus however long `initialize` takes. Building a snapshot adds roughly 10–30 seconds on top of your setup scripts' own runtime, paid once per build instead of once per machine. If your `initialize` takes five minutes, snapshots are the difference between an agent that starts working immediately and one that spends its first five minutes installing dependencies, on every thread, every task machine, every review. **Snapshots never gate a fresh boot.** Missing, stale, disabled, or failed, the fallback is always the same: a fresh machine runs your full setup. A snapshot is a cache of your setup, never a second source of truth: your configuration stays in charge, and the snapshot just caches the result of running it. ### Turning it on Enable snapshots in the **Snapshots** section of your project's Dev environment page, or ask Capy to do it. Enabling doesn't start a build by itself; the hourly check picks it up on its next pass. Trigger a build immediately from the same page, or ask Capy to build one now. There's also a test lane that runs the full build without activating the result, useful for validating setup changes while snapshots stay disabled. ### When snapshots rebuild Every hour, Capy compares the frozen build input (the selected repositories at their current commits, the active setup version, the machine size) against what the current snapshot was built from. Anything moved, it rebuilds; nothing moved, it doesn't. In practice: * New commits on a selected repository's base branch trigger a rebuild within the hour. * Changing setup scripts or machine size triggers a rebuild within the hour. * Identical input never rebuilds, so a quiet project builds nothing. * A failed build leaves the previous snapshot serving and is retried on the next hourly pass. Machines fall back to full setup only when there's no current snapshot at all. A build runs on a dedicated build machine at your configured size, follows your setup exactly, and activates on success. Snapshot build machines aren't billed to your organization. ### Choosing what goes in A snapshot contains a selected subset of the project's repositories, not necessarily all of them. Repositories outside the selection stay in the catalog and clone on demand when a thread needs them; they just don't get the instant-boot treatment. Selection is validated against the machine's disk budget using each repository's size from GitHub, and an over-budget selection is refused up front with a per-repo breakdown rather than failing halfway through a build. Each repository row on the Dev environment page carries an include-in-snapshot switch. A newly added project repository isn't snapshotted until you select it. ### Machine sizes and disk tiers Disk size bakes into the image, so a snapshot serves one disk tier: 64 GB covers Small through Ultra, 128 GB is Hyper, 256 GB is Big Guy. Within a tier, one snapshot serves equal or smaller machines than it was built at; a thread requesting a bigger machine than the snapshot's baked size falls back to fresh setup rather than silently booting undersized. If your setup size is on a bigger tier, the **also snapshot smaller machine sizes** option bakes the smaller tiers too, each built at that tier's largest size. ### Secrets in builds Builds receive shared project variables so `initialize` can authenticate private registries, and Capy scrubs and verifies every platform-managed secret path before capture; a build that can't prove the scrub fails. The one hole Capy can't close is a setup script that writes a secret into the workspace; details in [secrets](/secrets#snapshots-and-scrubbing). ### When a build fails Open the build from the Dev environment page's build history: every build records per-step logs, and each setup phase's output survives with the failing line intact. Or just ask Capy why the last snapshot build failed; it reads the same build history and logs, and can fix the setup and rebuild in one go. The failure costs you speed, not availability: machines keep booting fresh with full setup until a build succeeds. # Instructions Source: https://docs.capy.ai/instructions Capy reads `AGENTS.md` instructions from repositories and [volumes](/volumes). ## Instructions for every project Global instructions live in a volume's root `AGENTS.md`: **Personal** for your threads across projects, or **Organization** for team-wide rules. Both apply within the current organization, outside git. > Add “Use conventional-commit PR titles” to AGENTS.md in my personal volume. Preserve existing instructions. Inspect files under **More → Volumes**. See [Volumes](/volumes) for scopes and shared files. ## How instructions are loaded Repository instructions load from three sources: | Source | Files | When it's loaded | | ------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------- | | Root instructions | `AGENTS.md` or `CLAUDE.md` at the repo root | Thread start | | Nested instructions | `AGENTS.md` in any subdirectory | First time the agent touches a file in that directory's subtree | | Glob rules | `.capy/rules/*.{md,mdc}`, `.cursor/rules/*.{md,mdc}` | Thread start if `alwaysApply: true`, otherwise on first matching file touch | ### Root instructions Every agent reads the repo root's `AGENTS.md`, falling back to `CLAUDE.md` when no `AGENTS.md` exists. Per-agent instruction files (`.capy/BUILD.md`, `.capy/CAPTAIN.md`, `.capy/REVIEW.md`) are deprecated and no longer read; put agent guidance in `AGENTS.md`, nested files, or glob rules instead. ### Nested AGENTS.md An `AGENTS.md` anywhere below the repo root is discovered automatically. It isn't loaded at thread start; it's injected into context the first time the agent reads, edits, searches, or lists a file inside that directory's subtree. A thread that never enters `packages/api/` never pays the context cost of `packages/api/AGENTS.md`. Behavior to know: * **Lazy, then sticky.** Each nested file is injected at most once per thread, on first contact with its subtree. * **Deeper wins.** When instructions conflict, a more deeply nested `AGENTS.md` takes precedence over shallower ones (and nested files add to, not replace, the root file). * **File tools only.** Injection is triggered by file-level operations: reading, editing, searching, and listing files. Shell commands do not trigger nested injection, so a repo-wide grep from the terminal won't pull in every nested file at once. * **Plain markdown.** The whole file is treated as instructions. Frontmatter is not parsed; if you need globs or `alwaysApply`, use a [glob rule](#glob-rules) instead. Discovery follows git: only tracked files are found, so anything gitignored never enters context. Root instructions and skill descriptions share one context budget; oversized content is truncated or shortened with a visible marker rather than silently dropped. ### Glob rules Capy reads Cursor-style rules from `.capy/rules/` and `.cursor/rules/` (`.md` or `.mdc` files). YAML frontmatter controls when each rule loads: a rule needs `alwaysApply: true` or at least one `globs` pattern to be injected: ```markdown theme={null} --- description: SQL migration conventions globs: - "**/*.sql" alwaysApply: false --- - Every migration must be reversible - Never drop columns without an explicit approval comment ``` * `alwaysApply: true` loads at thread start * `globs` injects the first time the agent touches a matching file * Rules are injected with their file path attached, and each rule is injected at most once per thread Use `.capy/rules/` for Capy-native rules; `.cursor/rules/` is read natively so existing Cursor setups work unchanged. ### Skills For deep, on-demand workflows (deploy runbooks, codegen procedures, large reference material), use [skills](/skills) instead of instruction files. Skills advertise only their name and description until an agent decides to load one, so they cost almost nothing when unused. Instruction files answer "how should you always behave here?"; skills answer "how do I do this specific thing?". ## What goes where The failure mode to avoid is one giant root file that every thread pays for. Match each piece of guidance to the narrowest mechanism that still reaches it: | Guidance | Put it in | Why | | -------------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------- | | Stack summary, build/test/format commands, PR conventions, hard rules | Root `AGENTS.md` | Every thread needs it | | Per-package architecture, domain rules, local commands | Nested `AGENTS.md` in that package | Only loaded when working there | | File-type conventions that cut across directories (`**/*.sql`, `**/*.tsx`) | Glob rule | Directory-scoped files can't express "all SQL files" | | Multi-step workflows, long reference docs | [Skill](/skills) | Loaded on demand, keeps always-on context lean | **Root `AGENTS.md`** carries cross-cutting truths: what the project is, how to install/build/test/lint, commit and PR conventions, and non-negotiable rules ("always use pnpm", "never commit generated files"). Keep it lean: a bloated root file taxes every request in every thread, and long instruction files dilute the agent's attention on the rules that matter. **Nested `AGENTS.md`** carries what's true only inside a subtree. In a monorepo, keep the top-level file general and add a specific `AGENTS.md` per major subproject: `packages/api/AGENTS.md` for API conventions, `packages/web/AGENTS.md` for frontend rules. This is the same layout Amp and other agents recommend, so one set of files serves every tool. **Glob rules** carry concerns that follow a file type rather than a directory: design-token rules for every `.tsx` file, migration rules for every `.sql` file. If the concern maps cleanly to one directory, prefer a nested `AGENTS.md` there instead. ### Anti-patterns * **Duplicating a rule in multiple files.** State it once in the narrowest scope that covers it. Duplicates drift, and the agent has to reconcile conflicting copies. * **Restating what the code already says.** Don't enumerate exports, list every directory, or paraphrase type signatures; the agent reads code. Document what it can't infer: intent, invariants, and commands. * **Prompt-sized essays.** Ten sharp bullets outperform three pages of prose. "Use pnpm, not npm" beats "please make sure to use the correct package manager for this project". * **Secrets or credentials.** Instruction files are injected into model context, and repository instructions are also committed to git. Never put tokens, passwords, or internal URLs that shouldn't leak into them. * **Per-file trivia.** "This function retries three times because the upstream API flakes" belongs in a code comment next to the function, not in an instruction file. ## Examples ### A lean root AGENTS.md ```markdown theme={null} # AGENTS.md Acme is a TypeScript monorepo: `packages/api` (Fastify + Postgres), `packages/web` (React + Vite), `packages/shared` (types and utils). ## Commands - Install: `pnpm install` - Build: `pnpm build` - Test: `pnpm test` (single package: `pnpm --filter @acme/api test`) - Lint + format: `pnpm check`; run before finishing any task ## Rules - Always use pnpm, never npm or yarn - No `any` types; fix type errors instead of suppressing them - New env vars must be added to `.env.example` and `packages/shared/src/env.ts` - PR titles follow `type(scope): description` (e.g. `fix(api): handle null user`) ``` ### A nested packages/api/AGENTS.md ```markdown theme={null} # API package Fastify server with route handlers in `src/routes/`, business logic in `src/services/`, and data access in `src/db/`. - Route handlers validate input with zod schemas from `src/schemas/` and stay thin; logic lives in services - All queries go through the repository layer in `src/db/`; never import the client directly in a route or service - Every table uses soft deletes (`deleted_at`); filter it in every query - Integration tests live in `src/__tests__/` and run against the Docker Postgres from `pnpm db:test` ``` ### A glob rule ```markdown theme={null} --- description: Design token rules for React components globs: - "**/*.tsx" --- - Use tokens from `packages/web/src/styles/tokens.css`, never raw hex colors or arbitrary Tailwind values - Spacing comes from the `--space-*` scale; font sizes from `text-sm` through `text-2xl` only ``` ## Migrating from other tools * **From Claude Code**: nothing required; Capy reads root `CLAUDE.md` as a fallback. To standardize on `AGENTS.md` while keeping Claude Code working: `mv CLAUDE.md AGENTS.md && ln -s AGENTS.md CLAUDE.md` * **From Cursor**: keep `.cursor/rules/` where it is; Capy reads it natively, including `globs` and `alwaysApply` frontmatter * **From other agents using AGENTS.md** (Amp, Codex, Devin, ...): nothing required; root and nested `AGENTS.md` files work as-is Changes to repository instruction files apply to the next thread or task. # GitHub Source: https://docs.capy.ai/integrations/github GitHub is Capy's primary integration. The GitHub App gives your threads repository access, lets Capy open pull requests, and delivers the webhook events (CI results, review feedback, comments, merges) that wake the owning thread without you relaying anything. Connect it first; most of Capy's autonomy depends on it. ## Connect GitHub Go to [Settings → Integrations → GitHub](https://capy.ai/settings/integrations/github) and install the Capy GitHub App on your GitHub account or organization. Grant access to all repositories or a selected set; you can change the selection later in GitHub's own app settings. Add the repositories a project should work in. Threads in that project clone them automatically when a machine starts. Link your own GitHub account in the same settings page. With it connected, commits Capy makes on your behalf carry your name and pull requests open as you; without it, both are attributed to the Capy app. ## How Capy authenticates No GitHub token ever sits on the machine. The machine holds a short-lived signed grant (Capy's own credential, useless against GitHub directly), and every `git` and `gh` call routes through Capy's git proxy, which attaches the real GitHub credential server-side per request. Grants live about two hours and refresh automatically; you never manage them. The practical consequences: * `git` and `gh` work normally on the machine, including private repos, submodules, and private dependencies the installation covers. * Public repos and package-manager dependencies work exactly as they would with direct access. * Anything that expects a raw token fails closed: `curl -H "Authorization: token $GH_TOKEN"` fails because no token exists. Tell the agent to use `gh api` instead; it's the supported path. Three identities are resolved independently: the commit author is you when your GitHub account is connected (otherwise `Capy Agent`), the pull request author follows your configuration (your account when connected, the app otherwise), and the push itself is always authenticated as the app, which is what branch protection and the events feed show. ## Branches and pull requests Capy manages branches itself. When a thread publishes work, Capy mints a fresh `capy/` branch, pushes to it, and opens the pull request against the branch the thread started from: the repository default unless you asked for another base. You never check out, name, or clean up working branches, and Capy never pushes to your default branch directly. If a thread already published to its own `capy/` branch, later publishes reuse it and update the same pull request. Merging happens where you choose: from Capy's pull request view or on GitHub. ## GitHub events wake your threads Webhook events route to the thread that owns the pull request, so you don't babysit CI or forward feedback: * **CI.** A failing check wakes the thread immediately (fail-fast), and when the whole check set finishes, the thread gets one consolidated verdict instead of a ping per check. * **Review feedback.** Your reviews and comments on a Capy-opened PR wake the owning thread by default. Bot feedback is ignored by default; an org admin can allowlist specific bot accounts in settings. * **Merge.** Merging wakes the thread so it can finish up. A PR closed without merging becomes passive context: the thread sees it next time it runs but isn't woken for it. * **Conflicts.** A push to the base branch that makes an open Capy PR conflict notifies the owning thread. This only wakes threads that were recently active (3 days by default, configurable), so a base merge can't resurrect months-old threads. ## Talking to Capy from a pull request Mention Capy in a PR comment, top-level or inline. Mentions require that your GitHub account is connected to a Capy account with active membership in the org; mentions inside code blocks, links, or quoted text don't count. | You write | What happens | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `@capy ` | The message goes to the thread that owns the PR and wakes it. If no thread owns the PR, Capy starts a new thread in the project attached to the repository. | | `@capy review` | Starts a review round on the current head. Re-triggering on an already-reviewed head reuses the completed round instead of billing twice. | | `@capy review ` | Starts a review round with your trailing text as the run prompt. This forces a fresh round even when the head was already reviewed, so your instructions can't be swallowed by an old result. | ## Auto-review Auto-review is a per-repository mode set in Settings → Review: **Off**, **Once** (first review when a PR opens), or **Every push**. Drafts are skipped until they're ready and pushed. Enabling it means your org pays for the automatic rounds, and a manual `@capy review` bills the requesting org. [Review](/review) is the main page for modes, rounds, findings, and billing. ## What Capy posts back * Pull requests, opened from `capy/` branches under the identity you configured. * Review findings as inline comments at the exact head reviewed, posted as one batch review, covering findings at or above your severity threshold (default medium). Findings below the threshold stay in Capy's own record, where you see everything. * Thread resolution: when a finding is fixed or dismissed in Capy, the matching GitHub comment thread resolves. ## Permissions | Permission | Why Capy needs it | | -------------------------- | --------------------------------------------- | | Contents | Clone, fetch, and push through the git proxy | | Pull requests | Open PRs, read feedback, post review comments | | Checks and commit statuses | Receive CI results that wake threads | | Metadata | List repositories for project attachment | Repository reach follows the installation: a read-only installation can't write anything, and a repo the installation doesn't cover returns GitHub's own 404. ## Troubleshooting The installation's repository selection controls what Capy can see. Check the Capy app's settings on GitHub and make sure the repo is selected, then re-check the project's repository list. Your GitHub account must be connected to a Capy account that's an active member of the org, and the mention must be visible text; mentions inside code blocks, links, or quotes are ignored. Connect your account in Settings → Integrations → GitHub. CI events route to the thread that published the PR. A PR opened outside Capy, or with `gh pr create` instead of Capy's own publish path, may not have an owning thread; ask the thread to create the PR itself. Attribution follows your connected GitHub account. Connect or reconnect it in Settings → Integrations → GitHub; PRs opened after that carry your identity. Same cause: your GitHub account is disconnected or needs reauthorization. Until it's reconnected, Capy falls back to its own author identity rather than guessing. Expected: no reusable GitHub token exists on the machine by design. Route the same call through `gh`, which works via Capy's proxy. # Linear Source: https://docs.capy.ai/integrations/linear Connect Linear so your team can hand issues to Capy without leaving Linear. Delegating an issue starts a Capy thread, and everything the agent does streams back into the issue as native agent session activity: progress, responses, the plan, and a link to the thread. ## Connect Linear Go to [Settings → Integrations → Linear](https://capy.ai/settings/integrations/linear) and click **Install**. Any org member can connect; one Linear workspace connects to one Capy org. Map Linear teams to Capy projects and set a default project. Changing these settings requires an org admin or owner. Each person links their Linear account to their Capy account once. Capy prompts unlinked users in Linear the first time they delegate; nothing is matched by email or name automatically, so the link is always explicit. ## How delegation works Assigning or delegating an issue to the Capy agent creates a native Linear agent session, and that session maps one-to-one to a Capy thread. Later prompts and comments inside the agent session continue the same thread. The session is the boundary: a comment on an ordinary issue with no Capy agent session does nothing. If you want Capy on an issue, delegate it; mentioning or commenting won't start anything. When a session starts, Capy may move the issue into the team's first started state and set itself as the assignee when nobody else is assigned. The first prompt carries the issue identifier, title, description, URL, and requester, so the thread starts with real context; later prompts carry only the new comment. ## Project routing A new session picks its Capy project deterministically: 1. The Linear team's mapping, when one exists. 2. Otherwise the connection's default project. 3. Otherwise a persistent error in Linear pointing at Capy's connection settings; Capy never guesses a project. Routing fails closed: a stale mapping, or a mapping the delegating user can't access, produces a visible configuration error instead of silently landing in a different project. An existing session keeps its project even if mappings change later. ## Default branches Set per-repository default branches for Linear-origin threads in the connection settings. A repository without an entry falls back to its project default. These apply only to repositories already attached to the selected project. ## What Capy posts back Capy projects thread activity into the agent session as native Linear activity: * Short thought and action updates while the agent works. * Persistent responses for each message the agent sends you. * A persistent error activity when a run fails. * A selection prompt when the agent needs a clarifying answer. * The agent's current todo list as the session plan. * The Capy thread URL as the session's external link, so you can jump into the full transcript any time. Markdown and file attachments are lowered to what Linear supports, with files as attachment links. ## Stopping a session Linear's stop signal interrupts the thread's current run. It doesn't archive the thread or break the session mapping; a later prompt in the same session starts a fresh run. ## Permissions and scope * The connection is org-level: one Linear workspace per Capy org. * Any enabled member can install, reconnect, or disconnect the connection; team mappings, the default project, and branch defaults require an admin or owner. * Every inbound action re-checks the actor's identity link and current org membership: leaving the org revokes access immediately, and links don't grandfather. * If Linear revokes the app's authorization, the connection flips to a reconnect state that preserves your mappings; reconnecting restores everything. ## Troubleshooting Expected unless the issue has a Capy agent session. Delegate or assign the issue to the Capy agent first; comments inside that session then reach the thread. Your Linear account isn't linked to a Capy account yet. Follow the authentication prompt Capy posts in Linear; it's a one-time link, and your original prompt is delivered automatically once you complete it. Check the team mapping for the issue's team; without one, Capy uses the default project. An already-running session keeps its original project even after you fix the mapping. The mapped or default project is missing, or you don't have access to it. Capy deliberately refuses rather than picking another project; fix the mapping or your project access in Capy settings. Linear revoked or invalidated the authorization. Reinstall from Settings → Integrations → Linear; mappings and identity links survive the reconnect. # MCP Source: https://docs.capy.ai/integrations/mcp ## Overview Model Context Protocol (MCP) servers give Capy access to tools provided by external services and local processes. Once a server is connected, the capy agent (threads and their tasks) can discover and use its tools while working. The review agent runs a fixed tool set and never sees MCP tools. Configure MCP servers from **Settings → MCP servers** in a project. ## Add an MCP server Click **New MCP server** and give it a short key, like `internal-tools`. Save the server org-wide or narrowed to one project. This controls which threads see the server. For a remote server, enter its full HTTP endpoint. For a local server, enter the stdio command, arguments, working directory, and environment variables. If an HTTP server requires OAuth, open the saved server and click **Connect**. Servers that do not require authentication are ready after Capy discovers their tools. Each person can disable a configured server without removing it for everyone else. ## Availability | Availability | Who can use it | | ------------ | ------------------------------------------------- | | **Org-wide** | Everyone in the organization, across all projects | | **Project** | Everyone with access to the selected project | A server can carry either or both credential kinds: * **Personal connection**: credentials belong to one Capy user. When both personal and shared connections exist, Capy uses the current user’s personal connection. * **Shared connection**: credentials are available to everyone who can use the server. Only org admins can connect or disconnect shared credentials. ## Remote HTTP servers Choose **HTTP** and enter the server’s full MCP endpoint. Capy speaks every published MCP protocol revision: it probes with the modern protocol first, and if the server answers as an older one, it falls back through the legacy handshake down to the original HTTP+SSE transport. You don't configure the revision; Capy detects it per server and caches the answer. For servers that use a static token or API key, add the required HTTP header and paste the value itself. Header values are sealed at rest like any other Capy credential and sent only to that server. They are not interpolated, so an environment-variable reference like `$MY_TOKEN` would be sent literally and fail authentication. ```text theme={null} Server key: internal-tools URL: https://tools.example.com/mcp Header: Authorization Value: Bearer ``` ## Local stdio servers Choose **Stdio** for an MCP server that runs as a process on the thread's machine. Configure its command, arguments, working directory, and environment variables. ```text theme={null} Server key: local-docs Command: npx Arguments: -y, @example/docs-mcp ``` The command and its dependencies must be available on the project’s machines. Add installation steps to the project’s [Dev environment](/environment) when needed. Stdio servers do not use the browser OAuth flow. Provide any required credentials through environment variables. ## Transport and OAuth support | Capability | Support | | ---------------------------------------- | ------------------------------------------------------------------------------------ | | Protocol revisions | All five published revisions, 2024-11-05 through 2026-07-28 | | Modern protocol (2026-07-28) | Probed first; self-contained requests, no sessions | | Legacy protocol (2025-11-25 and earlier) | Fallback via the `initialize` handshake, including the 2024-11-05 HTTP+SSE transport | | Stdio | Supported inside the thread's machine | | MCP OAuth 2025-06-18 | Supported | | PKCE | Supported with `S256` | | Dynamic Client Registration | Supported | | OAuth Client ID Metadata Documents | Supported when advertised | For protected HTTP servers, Capy supports the MCP OAuth authorization flow from the 2025-06-18 specification, including: * OAuth 2.1 authorization code flow with PKCE * OAuth Protected Resource Metadata and Authorization Server Metadata discovery * OAuth resource indicators in authorization and token requests * Dynamic Client Registration * Public clients with `token_endpoint_auth_method: none` * Access-token refresh when the server issues a refresh token Capy can also use OAuth Client ID Metadata Documents when an authorization server advertises support. ## OAuth redirect URIs Authorization servers that enforce a redirect URI allowlist must register the following callback URI: ```text theme={null} https://capy.ai/web/callbacks/mcp ``` ## How agents use MCP tools MCP tools surface to the agent through its tool search, named `{server}__{tool}`: a server keyed `internal-tools` with a `query_db` tool appears as `internal-tools__query_db`. Only the capy agent gets them; review threads never do. Before calling a tool, Capy: 1. Connects to the configured server and lists its available tools. 2. Reads the selected tool’s input schema. 3. Calls the tool with schema-valid arguments. If a server reports that authentication is required, the affected call fails with a message telling the agent an admin must connect the server, and the thread's failure card links to settings with the server preselected. Connect only servers you trust, and keep API keys or static tokens in project environment variables rather than source control. # Slack Source: https://docs.capy.ai/integrations/slack ## Overview Connect Slack to start Capy threads directly from messages, sync conversations, and let agents read and search your Slack workspace for context. ## Installing the Slack app Go to [Settings → Integrations → Slack](https://capy.ai/settings/integrations/slack). You need to be an org admin. Click **Install** and authorize Capy in your Slack workspace. This connects the workspace for your entire organization. Choose a default project for Slack-originated threads. This is used for DMs and channels without an explicit mapping. ## Starting threads from Slack Mention **@Capy** in any channel where the app is installed. Capy starts a thread from your message and responds in a Slack thread. You can continue the conversation there - messages are synced to the Capy thread. You can also share images and files in the message. Capy will pick them up as context for the thread. ## Thread controls Use thread controls to manage what Capy sees and when it responds inside a Slack thread. ### Aside messages Prefix a message with `aside` to exclude it from Capy's processing entirely. * Capy won't see the message * Capy won't respond to it * It won't appear in the agent's context when Capy reads thread history This works with `aside ...` and `@Capy aside ...`. The check is case-insensitive, and `aside` must be at the start of the message. Use `aside` when you want to talk with teammates in a Capy thread without the agent picking those messages up. ### Mute and unmute Mute a thread to stop Capy from responding to regular thread replies. * Type `mute` or `@Capy mute` in the thread to mute it * Type `unmute` or `@Capy unmute` to turn replies back on * When muted, Capy ignores regular thread replies * Direct `@Capy` mentions still go through * DMs still work Capy confirms the change with an ephemeral message visible only to you: * `Thread muted - Capy will ignore messages here unless directly mentioned with @Capy.` * `Thread unmuted - Capy will respond to messages in this thread again.` If the thread is already in the requested state, Capy does nothing. Mute state is stored per thread. ### Archive Archive a Slack thread to stop Capy and close the Capy thread. * Type `archive` or `@Capy archive` in the thread * Capy stops any running work and archives the Capy thread * Later messages and direct mentions in the archived Slack thread are ignored and do not unarchive it * Type `unarchive` or `@Capy unarchive` to explicitly reopen it * To keep the archived thread closed, start a new `@Capy` mention outside the old Slack thread Capy confirms with: `Thread archived - Capy has stopped and this session is closed.` When explicitly reopened, Capy confirms with: `Thread unarchived - Capy will respond to messages here again.` ### Stop Stop the current run without archiving the thread. * Type `stop` or `@Capy stop` in the thread * Capy stops the current run and keeps the thread active * Send another message in the thread when you want Capy to start again Capy confirms with `Capy stopped the current run.` If no run is active, it replies with `Capy is not currently running.` ### Unlink from Slack Type `unlink` or `@Capy unlink` as the whole reply to pause Slack sync for the current thread. The Capy thread keeps running on web, and Slack receives a pause notice with an **Open in Capy** link. Resume sync from the thread's Slack control on web. ### Start a separate web thread Add `!web` anywhere in a message to start a separate thread on web without linking it to Slack. In an existing Capy Slack thread, the new thread starts with the source transcript while the original thread and its Slack sync remain unchanged. In an ordinary Slack conversation, mention `@Capy`; Capy reads a bounded snapshot as context, then leaves that conversation unlinked. Muted and archived Capy threads also require an explicit `@Capy` mention. Capy posts a **Started on web** card with an **Open in Capy** link. Continue the new thread on web: later Slack replies never enter it, and its replies, status, pull-request cards, and completion stay off Slack. You can link it to a new Slack thread later from its web menu, but it does not reattach to the source conversation. ## Channel mappings Map specific Slack channels to Capy projects so threads started from those channels are automatically assigned to the right project. 1. Go to [Settings → Integrations → Slack](https://capy.ai/settings/integrations/slack) 2. Scroll to **Channel mappings** 3. Select a channel and a project, then click **Save** Channels without a mapping fall back to the default project. DMs also use the default project. Capy also offers to set defaults for you. When neither the channel nor the workspace has a default and Capy has to ask you to pick a project, an admin's pick is followed by a one-time offer: make that project the default everywhere, only for this channel, or don't ask again. Every choice is reversible on the Slack settings page. ## Message directives Configure a thread with `key=value` directives anywhere in your `@Capy` message. Directives are stripped from the prompt before the agent sees it, and quoted values are supported. | Directive | Values | Description | | ----------- | -------------------------------------------- | ---------------------------------------------- | | `project` | project name or ID | Start the thread in a specific Capy project | | `folder` | folder name | File the new thread into a dashboard folder | | `channel` | `#channel` / name | Start the Capy thread in another Slack channel | | `title` | any text | Set the thread title | | `model` | model ID or shortcut | Run the thread on a specific model | | `reasoning` | `off`, `minimal`, `low` through `max` | Set the model's reasoning effort | | `machine` | `small`, `medium`, `large`, `ultra`, `hyper` | Set the machine size for a new thread or fork | ```text theme={null} @Capy project="Payments platform" fix the deploy script @Capy model=opus reasoning=high investigate the production incident @Capy channel=at-capy title="Bug report triage" investigate this @Capy machine=hyper !fast run the full test suite @Capy !web investigate this on web ``` `project`, `folder`, `title`, and `machine` apply when Capy creates a thread, including `channel=`, `!new`, and `!web` starts. A `machine=` directive on a normal continuation is refused because an existing thread's machine cannot be resized. Project names match case-insensitively with a safe fuzzy match; when the match is uncertain, Capy asks you to pick instead of guessing. `model=` accepts a full catalog ID (`openai/gpt-5.6-sol`), a model name without its route prefix (`gpt-5.6-terra`), or a shortcut phrase: `fable`, `opus`, `sonnet`, `haiku`, `sol`, `terra`, `luna`, `gemini`, `grok`, `kimi`, `qwen`, `glm`. A phrase served by more than one connected route (Capy-billed vs a subscription) asks you to pick a route once for the org; a phrase that matches nothing gets an error reply, never a silent guess. A reasoning effort or serving mode can ride on the phrase as trailing words: `model=luna-xhigh-fast` means the same as `model=luna !xhigh !fast`. Use whitespace-delimited bang flags for thread, model, and delivery controls: | Flag | Effect | | ---------------------------------------------------------------- | --------------------------------------------------------------- | | `!new` | Fork into a separate Slack thread in the current conversation | | `!web` | Start a separate web thread without Slack sync | | `!fast` | Enable Fast mode and clear Pro mode | | `!pro` | Enable Pro mode, clear Fast mode, and clear inherited reasoning | | `!standard` | Clear Fast and Pro while preserving the model and reasoning | | `!off`, `!minimal`, `!low`, `!medium`, `!high`, `!xhigh`, `!max` | Set reasoning effort | | `!queue` | Queue the prompt behind the active run | | `!steer` | Steer the active run without interrupting it | Capy refuses conflicting mode flags and an explicit Pro-plus-reasoning combination. A mode-only reply in an existing thread updates later replies and confirms the new selection once. `!new` and `!web` also work in an existing active Capy thread without a mention. `!new` creates a new Slack thread, while `!web` creates a web-origin thread with the source conversation as context. `!web` cannot be combined with `!new` or `channel=`. Repository and branch instructions are ordinary task context, not directives. Write them naturally - "use the backend repository on the staging branch" - and the agent handles them inside the selected project. ## Personal connection Team members can link their Slack identity to their Capy account from [Settings → Integrations → Slack](https://capy.ai/settings/integrations/slack). Linking attributes your Slack messages to your Capy user - threads you start from Slack belong to you on the dashboard, and replies carry your name into the agent's context. Unlinked teammates can still talk to Capy in threads; their messages just aren't tied to a Capy account. ## Pull request cards When a Slack-started thread opens or takes over a pull request, Capy posts a card for it into the Slack thread and keeps it current from GitHub state - status, checks, branches, and diff totals update in place, so the card never goes stale as work continues. * One card per pull request per thread. A thread working across five PRs gets five cards, each updating independently. * Cards carry **Open in Capy** and **View on GitHub** actions. * Merging or closing a PR posts one compact follow-up sentence, so you're notified even after the card has scrolled away. Everything else - checks progressing, drafts flipping ready, reopens - only updates the card. * A muted thread gets no new cards, but its existing cards keep updating: muting silences conversation, not a status object you can already see. Pull request links inside Capy's messages open the Capy pull request view by default. Prefer GitHub? Switch the link destination on the Slack settings page - the preference is captured per thread when it starts, so old messages keep their original destinations. ## What agents can do with Slack When a thread is started from Slack, agents have access to these capabilities: * **Read messages** from public channels and private channels where the Capy bot is a member * **Search messages** across the workspace * **Send messages** to channels or threads, with optional file attachments * **React to messages** with emoji * **List channels** to find the right conversation Agents always respond in the Slack thread that started the Capy thread. They can also send messages to other channels when needed. ## Permissions | Permission | Purpose | | ------------------------------- | ---------------------------- | | Chat:write | Post messages and replies | | Channels:read | List and read channels | | Reactions:read, Reactions:write | Sync and add emoji reactions | | Files:write | Upload file attachments | | Users:read | Resolve display names | ## Troubleshooting Make sure the Capy app is installed in the channel. Type `/invite @Capy` to add it. Also verify the Slack workspace is connected in Settings → Integrations → Slack. Check your channel mappings. If the channel isn't mapped, threads use the default project. Set one in Settings → Integrations → Slack. The thread may be muted. Type `unmute` or `@Capy unmute` in the thread to re-enable normal replies. Direct `@Capy` mentions still work while a thread is muted. That's expected. Messages starting with `aside` are excluded from Capy's processing and do not appear in the thread context the agent reads later. The Capy bot reads private channels only where it is a member. Invite it with `/invite @Capy` in that channel. # Tailscale Source: https://docs.capy.ai/integrations/tailscale Connect [Tailscale](https://tailscale.com/) so agent machines can securely reach private services inside your network: internal databases, staging APIs, package registries, anything on your tailnet. Machines in enabled projects join your tailnet as [ephemeral nodes](https://tailscale.com/kb/1111/ephemeral-nodes) at boot, tagged with a tag you choose, so you control exactly what they can access with your Tailscale ACLs. No public exposure, no static IP allow lists. ## Connect your tailnet In your [tailnet policy file](https://login.tailscale.com/admin/acls), add a `tagOwners` entry for the tag Capy machines will carry. We recommend `tag:capy`: ```json theme={null} "tagOwners": { "tag:capy": ["autogroup:admin"], } ``` The tag lets your ACL rules single out Capy machines; the owner list is who may assign it. On the [Trust credentials](https://login.tailscale.com/admin/settings/trust-credentials) page, click **Credential** and choose **OAuth**: Tailscale's new credential page with the OAuth option selected On the Scopes step pick **Custom scopes**, expand the **Keys** section, and check **Write** on **Auth Keys**; the tag picker that appears is required for the write scope — select the tag from the previous step. Leave every other scope unchecked: The Auth Keys scope with Write checked and tag:capy selected Generate the credential and copy the client ID and secret; the secret is shown once and starts with `tskey-client-`. Go to [Settings → Integrations → Tailscale](https://capy.ai/settings/integrations/tailscale), click **Connect**, and enter the client ID, client secret, and your tag. Capy validates the credentials against your tailnet before saving, and the secret is stored encrypted. Any org member can connect; each Capy org holds one Tailscale connection. The **Projects** section appears on the same page once connected. Turn on each project whose machines should join your tailnet; machines in other projects never join. ## How it works When a machine in an enabled project boots or wakes, Capy mints a single-use, short-lived auth key from your OAuth client and the machine joins your tailnet as an ephemeral node named `capy-`, carrying your tag. From that point, agent commands on the machine can reach whatever your ACLs allow. * **Ephemeral and self-cleaning.** Nodes are removed by Tailscale automatically when a machine pauses or is destroyed; a resumed machine rejoins with a fresh key. Your machine list never accumulates stale entries. * **Never blocking.** A failed join never blocks a machine from booting; the machine runs without tailnet access and rejoins on its next wake. * **Keys stay on the machine.** Each auth key is minted per machine, expires after five minutes, and is delivered to a root-owned file readable by no other user. Keys never appear in process arguments, environment variables, or logs, and never persist into machine snapshots. ## Controlling access with ACLs Capy machines have exactly the access your ACL grants their tag, and nothing else by default. For example, to let agents reach a private Postgres instance, declare its address as a host alias and accept traffic to its port: ```json theme={null} "hosts": { "internal-db": "100.101.102.103", }, "acls": [ { "action": "accept", "src": ["tag:capy"], "dst": ["internal-db:5432"] }, ] ``` A destination can be a `hosts` alias, a Tailscale IP or CIDR, or another tag (for example `tag:database:5432` if your database nodes are themselves on the tailnet). Scope the tag's rules to the specific services agents need; there is no reason for Capy machines to see your whole tailnet. ## Disconnecting Turning a project off stops its machines from joining at their next boot or wake. Disconnecting the integration (the **⋯** menu on the connection card) removes the stored credentials and stops all new joins immediately. Either way, already-running machines keep their tailnet session until they pause or stop. To cut one off immediately, remove its node from your [Tailscale machines list](https://login.tailscale.com/admin/machines), and revoke the OAuth client in Tailscale if the credentials themselves should die. ## Troubleshooting Capy verifies the client by minting and immediately revoking a probe key. Check that the OAuth client has **Keys → Auth Keys** write scope, that the tag on the scope matches the tag you entered, and that the tag exists in your policy file's `tagOwners`. Your tailnet definitively rejected the credentials, usually because the OAuth client was revoked or the tag was removed from `tagOwners`. Machines keep booting without tailnet access until you reconnect with working credentials. Check that its project's toggle is on; only machines booted or woken after enabling join. A machine whose join failed rejoins on its next wake, so pausing and resuming it retries immediately. That's an ACL question: nodes tagged with your tag need an `accept` rule to the service's host and port. Test from the machine with `tailscale ping `. No; machine infrastructure uses dynamic addresses. The tag is the stable identity, which is exactly what the ACL rules key on. # Vercel Source: https://docs.capy.ai/integrations/vercel ## Overview Capy detects Vercel preview deployments automatically and surfaces them in the pull request view. Review code diffs and the live preview side by side, with no webhooks, API keys, or configuration. ## How it works When Vercel deploys a preview from a PR branch, Capy picks up the deployment from the PR's comments and its Vercel check, and shows a **Preview** button in the pull request view with the build state folded into it: ready, building, or failed. The button links to the best available URL, in this order: 1. A markdown link labeled "preview" in a PR comment beats a bare preview-host URL, and a stable `-git-` branch alias beats a per-deployment URL. GitHub-hosted URLs never qualify. 2. The Vercel check's own details URL, when it points at the deployment rather than the vercel.com dashboard. 3. As a last resort, the dashboard's inspect page for the deployment; one click from the preview beats a dead button. Link your Vercel project to GitHub. When Vercel deploys previews on PRs, Capy detects them automatically. An agent works on your thread and opens a PR. Vercel triggers a preview deployment from the branch. Click **Preview** in the pull request view to open the live deployment. No configuration is required. If Vercel is connected to your GitHub repo, it just works. ## In-machine previews For previewing during a thread *before* a PR exists, ask the agent to start your service and open it in the machine's browser, and watch the machine's desktop view live from the thread. ## Full workflow 1. Start an in-machine service so the agent validates changes while coding 2. Agent opens a PR when done 3. Vercel deploys a preview from the branch 4. You review code and the live deployment from Capy's pull request view ## Limitations * Capy does not trigger Vercel deployments; your existing Vercel setup handles the deploy pipeline * The Preview button shows one link per PR, the best-ranked one; other deployments stay reachable from Vercel ## FAQ No. If Vercel is connected to your GitHub repo, Capy detects previews from PR comments and the Vercel check automatically. There's nothing to install or configure. No. Capy reads preview links but doesn't trigger deployments. Your existing Vercel setup handles the deploy pipeline. Previews still appear: the Vercel check's details URL and the dashboard inspect page are fallback sources, so the comment lane isn't required. The Preview button surfaces the single best link per PR. For multiple running services during a thread, the machine's Desktop services menu discovers in-machine HTTP services automatically. # Machines Source: https://docs.capy.ai/machines Every thread runs on a machine: an isolated cloud VM where the agent clones your repositories, runs commands, and edits code. The agent loop runs on Capy's servers; the machine is where execution happens. Tasks either share their parent thread's machine or boot their own, with the same lifecycle either way. **Pushed repository changes and files saved in [volumes](/volumes) survive machine replacement.** Unpushed checkout edits, installed packages, scratch files, and running processes are disposable machine state. ## A machine outlives its VM A machine is the durable identity a thread attaches to. The sandbox actually running behind it, the backing, can die and be rebuilt at any time. "The machine died" almost always means the backing went away: the provider expired it, or it sat idle past the retention window. The next use boots a fresh backing from your project's [dev environment](/environment) and the thread carries on with the same machine identity. This is why setup investment pays off. Your setup configuration is the recipe every rebuilt backing follows, and a [snapshot](/environment#snapshots) makes following that recipe nearly instant. ## How a machine boots 1. **A thread requests a machine.** This creates the identity only; no VM yet. Boot overlaps the model's first turn, so you rarely wait on it. 2. **A backing boots.** When your project has a current snapshot, the VM restores from it in roughly 1–2 seconds with dependencies already installed. Without one, a fresh Ubuntu VM boots and pays your full setup time. 3. **Repositories are cloned and checked out.** Shallow clones at each repository's base branch, routed through Capy's git proxy; no GitHub credentials ever land on the machine. 4. **Environment variables are written and setup scripts run.** [Variables](/secrets) are written into the machine environment first. A fresh clone runs your `initialize` script; a snapshot restore advances the checkout and runs `refresh` only when the commit actually moved. Every machine then launches your startup entries in order, each in its own terminal session. 5. **The agent works.** Tool calls execute on the machine, and the machine stays awake as long as the thread is working. If a setup script fails, the machine still comes up: the thread gets a workspace warning, the agent sees the failing repository and phase, and it will usually try to repair the workspace by hand. Fix the script in your dev environment so the next machine doesn't need repairing. ## Sleep A machine sleeps when nothing holds it awake. The rules: * While the agent works, the machine stays awake automatically. * About two minutes after the thread stops working, the machine goes to sleep. Background operations the agent is tracking (a long test run) hold it awake; detached processes (a dev server) do not. * **The filesystem survives sleep. Memory, processes, and network connections do not.** Resume is a cold boot from disk: your checkout and installed packages are intact, but every process is gone. * On the next wake, Capy refreshes the checkouts and runs your `startup` script again, so services configured there come back on their own. Anything started ad hoc stays dead until something restarts it. * The machine panel in a thread shows the sleep countdown and offers **+5 minutes**, a prepaid extension you can stack up to 30 minutes ahead. There is no stop button; an idle machine sleeps on its own within minutes. A sleeping machine costs nothing. Asleep 2 days, its filesystem moves to colder storage but still resumes. Asleep 30 days, it's deleted for good. Before you rely on that being fine, apply the rule: anything on that machine you haven't pushed is gone with it. ## Watching the machine The thread pane shows the machine as the agent uses it: terminal output for commands, the growing diff, a file browser, and a live desktop view of the machine's display, useful when the agent is driving a browser or you asked it to open your app. You never need SSH to see what's happening. ## Exposing ports Ask Capy to expose a port and it serves that TCP port at a public URL: the way to open a dev server the agent started. Two failure modes to know: * The URL works only while the machine is awake. Traffic to an exposed URL does not keep the machine awake; use the +5 minutes button or keep the thread working while you click around. * Ports 8000, 8080, 8100, and 22222 are reserved for Capy's own services; run your app on anything else. ## Moving files between machines Each task can run on its own machine, and machines don't share a filesystem. Capy copies files and directories between machines with its `transfer_files` tool, the only cross-machine path. Within one machine, plain `cp` does the job. If you need a build artifact from a task's machine, ask Capy to transfer it; don't expect it to appear on the primary machine by itself. ## Sizes and pricing Machine size is set per project in the [environment](/environment); every new machine in the project uses it, snapshot builds included. You pay by the hour while a machine is awake. Sizes run from Small (1 vCPU, 4 GB) through Big Guy (16 vCPU, 128 GB); the hourly rate for each lives in [models & pricing](/models-and-pricing#machine-pricing), the single source for rates. Large is the default and handles most work. Go bigger for heavy builds and large test suites; a size change applies to new machines, not ones already running. ## What's on the machine Every machine is an isolated Ubuntu 24.04 VM where the agent has full control: it edits files, runs commands, installs packages, and commits code, all without touching your computer. The agent runs as a user with passwordless sudo, so anything it can't find preinstalled it installs itself, or you install once in your [setup scripts](/environment) and bake into a snapshot. Preinstalled on every machine: * **Node.js 24** with npm and corepack-managed pnpm and yarn, plus **Bun** * **Python 3** with pip, pipx, and **uv** * **Docker** Engine, Compose, and buildx * **git** with git-lfs, and the **GitHub CLI** (`gh`) * Native build tools: gcc, make, cmake, ninja, pkg-config, and common dev headers * Everyday CLI tools: ripgrep, fd, fzf, tmux, rsync, sqlite3, zip/unzip, lsof, strace * A desktop with **Google Chrome**, which is what the agent's browser and screenshot tools drive Rust, Go, Java, or anything else: the agent installs it on demand, and your `initialize` script is the right place to make it permanent. ## Docker Tasks can build images, run service dependencies, and test containerized apps directly inside the VM: ```bash theme={null} docker run --rm hello-world docker compose up -d curl http://127.0.0.1:3000 ``` Containers can pull images and reach the public internet, resolve each other by service name on user-defined and Compose networks, and publish ports reachable from the VM. Docker's daemon is socket-activated, so it starts on first use rather than at boot. And like every process, running containers don't survive the machine going to [sleep](#sleep). Start service dependencies in your `startup` script so they come back on wake. Sandbox image changes apply to new machines. If a running thread's machine predates a capability, start a new thread to pick up the updated image. # Models & pricing Source: https://docs.capy.ai/models-and-pricing Capy bills for two things: model tokens and machine server time. Usage draws from one balance, in dollars, shared by your whole organization. A subscription plan includes a set amount of usage each month at a discount. You can also add balance directly (minimum \$5) and enable [auto-reload](/admin/billing#auto-reload). ## Plans Three plans, sized by the monthly credit grant. Annual billing is 12 months upfront at 20% off, with the same credits granted monthly. | Plan | Monthly | Annual (per month) | Credits per month | | -------------- | ------------- | ------------------ | ------------------------ | | **Capy Lite** | \$20 | \$16 | \$20 of credits | | **Capy Pro** | \$100 | \$80 | \$105 of credits (+5%) | | **Capy Max** | \$200–\$1,000 | \$160–\$800 | 110% of the price (+10%) | | **Enterprise** | Custom | Custom | Custom | Capy Max comes in \$100 steps from \$200 to \$1,000. Every step grants 110% of its price in credits, so \$500/mo buys \$550 of credits. **Try Capy Lite for \$1.** Your first 7 days cost \$1 with a verified card, then \$20/mo. One intro per card and per organization. Monthly Lite only. If you use your credits up mid-month, top up manually or with [auto-reload](/admin/billing#auto-reload). ## No seats Every plan includes unlimited members. There is no per-seat price, no included-member limit, and no charge for inviting anyone. Plans size usage, nothing else. ## Models Picking a model entry picks both the model and how usage gets billed. `openai/gpt-5.6-sol` bills your Capy balance at the rates below. `codex/gpt-5.6-sol` runs the same model on your ChatGPT subscription. Prices are per 1M tokens. Models marked BYOK can also run on your own provider API key from [Settings → Models](https://capy.ai/settings/models). | Model | ID | Context | Input (per 1M) | Output (per 1M) | BYOK | | -------------------------- | --------------------------------- | ------- | ------------------------------------------------------------------ | ------------------------------------ | ---- | | **GPT-6 Astra** | `openai/gpt-6-astra` | 1.05M | ≤272K: \$10.00 (cached \$1.00)
>272K: \$20.00 (cached \$2.00) | ≤272K: \$50.00
>272K: \$75.00 | ✓ | | **GPT-6 Sol** (default) | `openai/gpt-6-sol` | 1.05M | ≤272K: \$2.00 (cached \$0.20)
>272K: \$4.00 (cached \$0.40) | ≤272K: \$10.00
>272K: \$15.00 | ✓ | | **GPT-6 Luna** | `openai/gpt-6-luna` | 1.05M | ≤272K: \$0.10 (cached \$0.01)
>272K: \$0.20 (cached \$0.02) | ≤272K: \$0.50
>272K: \$0.75 | ✓ | | **GPT-5.6 Sol** | `openai/gpt-5.6-sol` | 1.05M | ≤272K: \$4.00 (cached \$0.40)
>272K: \$8.00 (cached \$0.80) | ≤272K: \$20.00
>272K: \$30.00 | ✓ | | **GPT-5.6 Terra** | `openai/gpt-5.6-terra` | 1.05M | ≤272K: \$2.00 (cached \$0.20)
>272K: \$4.00 (cached \$0.40) | ≤272K: \$12.00
>272K: \$18.00 | ✓ | | **GPT-5.6 Luna** | `openai/gpt-5.6-luna` | 1.05M | ≤272K: \$0.20 (cached \$0.02)
>272K: \$0.40 (cached \$0.04) | ≤272K: \$1.20
>272K: \$1.80 | ✓ | | **GPT-5.5** | `openai/gpt-5.5` | 1.05M | ≤272K: \$5.00 (cached \$0.50)
>272K: \$10.00 (cached \$1.00) | ≤272K: \$30.00
>272K: \$45.00 | ✓ | | **GPT-5.5 Pro** | `openai/gpt-5.5-pro` | 1.05M | ≤272K: \$30.00
>272K: \$60.00 | ≤272K: \$180.00
>272K: \$270.00 | ✓ | | **GPT-5.4 Mini** | `openai/gpt-5.4-mini` | 400K | \$0.75 (cached \$0.075) | \$4.50 | ✓ | | **GPT-5.4** | `openai/gpt-5.4` | 1.05M | ≤272K: \$2.50 (cached \$0.25)
>272K: \$5.00 (cached \$0.50) | ≤272K: \$15.00
>272K: \$22.50 | ✓ | | **GPT-5.3 Codex** | `openai/gpt-5.3-codex` | 400K | \$1.75 (cached \$0.175) | \$14.00 | ✓ | | **Claude Fable 5.1** | `anthropic/claude-fable-5-1` | 1M | \$10.00 (cached \$0.25) | \$50.00 | ✓ | | **Claude Fable 5** | `anthropic/claude-fable-5` | 1M | \$10.00 (cached \$1.00) | \$50.00 | ✓ | | **Claude Opus 5.5** | `anthropic/claude-opus-5-5` | 1M | \$4.00 (cached \$0.20) | \$20.00 | ✓ | | **Claude Opus 5** | `anthropic/claude-opus-5` | 1M | \$5.00 (cached \$0.50) | \$25.00 | ✓ | | **Claude Sonnet 5** | `anthropic/claude-sonnet-5` | 1M | \$2.00 (cached \$0.20) | \$10.00 | ✓ | | **Claude Haiku 4.5** | `anthropic/claude-haiku-4-5` | 200K | \$1.00 (cached \$0.10) | \$5.00 | ✓ | | **Claude Opus 4.8** | `anthropic/claude-opus-4-8` | 1M | \$5.00 (cached \$0.50) | \$25.00 | ✓ | | **Claude Opus 4.7** | `anthropic/claude-opus-4-7` | 1M | \$5.00 (cached \$0.50) | \$25.00 | ✓ | | **Claude Sonnet 4.6** | `anthropic/claude-sonnet-4-6` | 1M | \$3.00 (cached \$0.30) | \$15.00 | ✓ | | **Claude Opus 4.6** | `anthropic/claude-opus-4-6` | 1M | \$5.00 (cached \$0.50) | \$25.00 | ✓ | | **Claude Opus 4.5** | `anthropic/claude-opus-4-5` | 200K | \$5.00 (cached \$0.50) | \$25.00 | ✓ | | **Grok 4.7** | `xai/grok-4.7` | 500K | \<200K: \$2.00 (cached \$0.50)
≥200K: \$4.00 (cached \$1.00) | \<200K: \$6.00
≥200K: \$12.00 | ✓ | | **Grok 4.6** | `xai/grok-4.6` | 500K | \<200K: \$2.00 (cached \$0.50)
≥200K: \$4.00 (cached \$1.00) | \<200K: \$6.00
≥200K: \$12.00 | ✓ | | **Grok 4.5** | `xai/grok-4.5` | 500K | \<200K: \$2.00 (cached \$0.30)
≥200K: \$4.00 (cached \$0.60) | \<200K: \$6.00
≥200K: \$12.00 | ✓ | | **Kimi K3** | `moonshotai/kimi-k3` | 1.05M | \$3.00 (cached \$0.30) | \$15.00 | | | **Kimi K2.7 Code** | `moonshotai/kimi-k2.7-code` | 262K | \$0.95 (cached \$0.19) | \$4.00 | | | **Kimi K2.6** | `moonshotai/kimi-k2.6` | 262K | \$0.95 (cached \$0.16) | \$4.00 | | | **GLM-5.3** | `zai/glm-5.3` | 1.05M | \$1.40 (cached \$0.26) | \$4.40 | | | **GLM-5.3-Flash** | `zai/glm-5.3-flash` | 1.05M | \$0.15 (cached \$0.03) | \$0.50 | | | **GLM-5.2** | `zai/glm-5.2` | 1.05M | \$1.40 (cached \$0.26) | \$4.40 | | | **GLM-5V-Turbo** | `zai/glm-5v-turbo` | 203K | \$1.20 (cached \$0.24) | \$4.00 | | | **GLM-5-Turbo** | `zai/glm-5-turbo` | 203K | \$1.20 (cached \$0.24) | \$4.00 | | | **DeepSeek V4.1 Flash** | `deepseek/deepseek-v4.1-flash` | 1.05M | \$0.30 (cached \$0.006) | \$1.20 | | | **DeepSeek V4 Pro** | `deepseek/deepseek-v4-pro` | 1.05M | \$1.74 (cached \$0.145) | \$3.48 | | | **DeepSeek V4 Flash 0731** | `deepseek/deepseek-v4-flash-0731` | 1.05M | \$0.14 (cached \$0.028) | \$0.28 | | | **Gemini 3.8 Flash** | `google/gemini-3.8-flash` | 1.05M | \$0.75 (cached \$0.075) | \$3.75 | ✓ | | **Gemini 3.7 Flash** | `google/gemini-3.7-flash` | 1.05M | \$0.75 (cached \$0.075) | \$3.75 | ✓ | | **Gemini 3.6 Flash** | `google/gemini-3.6-flash` | 1.05M | \$1.50 (cached \$0.15) | \$7.50 | ✓ | | **Qwen3.8 Max** | `qwen/qwen3.8-max` | 1M | \$2.00 (cached \$0.25) | \$6.00 | | | **Qwen3.8 Flash** | `qwen/qwen3.8-flash` | 1M | \$0.15 (cached \$0.016) | \$0.47 | | | **Qwen3.7 Max** | `qwen/qwen3.7-max` | 1M | \$1.48 (cached \$0.295) | \$4.42 | | | **Qwen3.7 Plus** | `qwen/qwen3.7-plus` | 1M | ≤256K: \$0.32 (cached \$0.064)
>256K: \$0.96 (cached \$0.192) | ≤256K: \$1.28
>256K: \$3.84 | | | **Qwen3.6 Plus** | `qwen/qwen3.6-plus` | 1M | ≤256K: \$0.325
>256K: \$1.30 | ≤256K: \$1.95
>256K: \$3.90 | | | **MiniMax M3** | `minimax/minimax-m3` | 524K | \$0.30 (cached \$0.06) | \$1.20 | | | **MiniMax M2.7** | `minimax/minimax-m2.7` | 205K | \$0.30 (cached \$0.06) | \$1.20 | | | **MiMo-V2.5 Pro** | `xiaomi/mimo-v2.5-pro` | 1.05M | \$0.435 (cached \$0.0036) | \$0.87 | | | **MiMo-V2.5** | `xiaomi/mimo-v2.5` | 1.05M | \$0.14 (cached \$0.0028) | \$0.28 | | | **Hy4 Preview** | `tencent/hy4-preview` | 1.05M | \$0.834 (cached \$0.042) | \$2.50 | | | **Hy3** | `tencent/hy3` | 262K | \$0.132 (cached \$0.033) | \$0.528 | | | **LongCat-2.0** | `meituan/longcat-2.0` | 1.05M | \$0.30 (cached \$0.006) | \$1.20 | | | **Muse Spark 1.3** | `meta/muse-spark-1.3` | 1.05M | \$1.25 (cached \$0.15) | \$4.25 | | | **Muse Spark 1.2** | `meta/muse-spark-1.2` | 1.05M | \$1.25 (cached \$0.15) | \$4.25 | | | **Muse Spark 1.1** | `meta/muse-spark-1.1` | 1.05M | \$1.25 (cached \$0.15) | \$4.25 | | | **Muse Glimmer 30B** | `meta/muse-glimmer-30b` | 131K | \$0.30 (cached \$0.04) | \$1.10 | | ## OAuth subscriptions These entries run through a connected OAuth subscription (Codex, Copilot, SuperGrok) or an organization account (Azure). Tokens bill to that subscription instead of your Capy balance. | Model | ID | Runs through | Context | | ----------------------- | --------------------------- | -------------------- | ------- | | **GPT-6 Astra** | `codex/gpt-6-astra` | Codex | 400K | | **GPT-6 Sol** | `codex/gpt-6-sol` | Codex | 400K | | **GPT-6 Luna** | `codex/gpt-6-luna` | Codex | 400K | | **GPT-5.6 Sol** | `codex/gpt-5.6-sol` | Codex | 400K | | **GPT-5.6 Terra** | `codex/gpt-5.6-terra` | Codex | 400K | | **GPT-5.6 Luna** | `codex/gpt-5.6-luna` | Codex | 400K | | **GPT-5.5** | `codex/gpt-5.5` | Codex | 400K | | **GPT-5.4 Mini** | `codex/gpt-5.4-mini` | Codex | 400K | | **GPT-5.4** | `codex/gpt-5.4` | Codex | 400K | | **GPT-5.3 Codex Spark** | `codex/gpt-5.3-codex-spark` | Codex | 128K | | **GPT-5.6 Sol** | `copilot/gpt-5.6-sol` | Copilot | 1.05M | | **GPT-5.6 Terra** | `copilot/gpt-5.6-terra` | Copilot | 1.05M | | **GPT-5.6 Luna** | `copilot/gpt-5.6-luna` | Copilot | 1.05M | | **Claude Opus 4.8** | `copilot/claude-opus-4-8` | Copilot | 200K | | **GPT-5.5** | `copilot/gpt-5.5` | Copilot | 1.05M | | **Claude Opus 4.7** | `copilot/claude-opus-4-7` | Copilot | 200K | | **GPT-5.4 Mini** | `copilot/gpt-5.4-mini` | Copilot | 400K | | **GPT-5.4** | `copilot/gpt-5.4` | Copilot | 1.05M | | **Gemini 3.7 Flash** | `copilot/gemini-3.7-flash` | Copilot | 1M | | **Gemini 3.6 Flash** | `copilot/gemini-3.6-flash` | Copilot | 1M | | **Claude Sonnet 4.6** | `copilot/claude-sonnet-4-6` | Copilot | 200K | | **GPT-5.3 Codex** | `copilot/gpt-5.3-codex` | Copilot | 400K | | **Claude Opus 4.6** | `copilot/claude-opus-4-6` | Copilot | 200K | | **Claude Opus 4.5** | `copilot/claude-opus-4-5` | Copilot | 200K | | **Claude Haiku 4.5** | `copilot/claude-haiku-4-5` | Copilot | 200K | | **Grok 4.7** | `supergrok/grok-4.7` | SuperGrok | 500K | | **Grok 4.6** | `supergrok/grok-4.6` | SuperGrok | 500K | | **Grok 4.5** | `supergrok/grok-4.5` | SuperGrok | 500K | | **GPT-5.6 Sol** | `azure/gpt-5.6-sol` | Azure (organization) | 1.05M | | **Claude Fable 5** | `azure/claude-fable-5` | Azure (organization) | 1M | ## Fast mode Models with a Fast toggle run through the provider's priority tier at a higher price. When no fast host is available, the request serves at the standard tier and bills at standard rates. Fast rates per 1M tokens: | Model | Fast input (per 1M) | Fast output (per 1M) | | ------------------- | ----------------------------------------------------------------- | ----------------------------------- | | **GPT-6 Astra** | \$20.00 (cached \$2.00) | \$100.00 | | **GPT-6 Sol** | \$4.00 (cached \$0.40) | \$20.00 | | **GPT-6 Luna** | \$0.20 (cached \$0.02) | \$1.00 | | **GPT-5.6 Sol** | \$8.00 (cached \$0.80) | \$40.00 | | **GPT-5.6 Terra** | \$4.00 (cached \$0.40) | \$24.00 | | **GPT-5.6 Luna** | \$0.40 (cached \$0.04) | \$2.40 | | **GPT-5.5** | \$12.50 (cached \$1.25) | \$75.00 | | **GPT-5.4 Mini** | \$1.50 (cached \$0.15) | \$9.00 | | **GPT-5.4** | \$5.00 (cached \$0.50) | \$30.00 | | **GPT-5.3 Codex** | \$3.50 (cached \$0.35) | \$28.00 | | **Claude Opus 5.5** | \$8.00 (cached \$0.40) | \$40.00 | | **Claude Opus 5** | \$10.00 (cached \$1.00) | \$50.00 | | **Claude Opus 4.8** | \$10.00 (cached \$1.00) | \$50.00 | | **Grok 4.7** | \<200K: \$4.00 (cached \$1.00)
≥200K: \$8.00 (cached \$2.00) | \<200K: \$12.00
≥200K: \$24.00 | | **Grok 4.6** | \<200K: \$4.00 (cached \$1.00)
≥200K: \$8.00 (cached \$2.00) | \<200K: \$12.00
≥200K: \$24.00 | | **Grok 4.5** | \<200K: \$4.00 (cached \$0.60)
≥200K: \$8.00 (cached \$1.20) | \<200K: \$12.00
≥200K: \$24.00 | ## Reasoning effort Most models expose a reasoning effort, from `none` up to `max`. Each entry supports its own subset, shown in the picker. An unsupported effort fails before the request is sent. Capy never quietly approximates it with a different setting. ## Bring your own key (BYOK) BYOK can be configured for enterprise organizations, replacing standard Capy model routing with your own configured key and API endpoint. Activate it in [Settings → Models](https://capy.ai/settings/models) with your provider API key. Your key pays the provider directly, and Capy adds no model charge for that route. ## Per-task models Threads and tasks pick models independently. The agent can run a task on a cheaper model for mechanical work, or a stronger one for a hard subsystem. Tasks follow parent thread models by default. ## When your balance runs out A run that can't pass the balance check stops with a visible error, recorded in the thread. Add balance manually or with [auto-reload](/admin/billing#auto-reload), then retry or resend. The thread picks up from its recorded history. ## Machine pricing Each thread runs on its own VM machine, billed hourly by size: | Size | vCPU | RAM | Disk | Cost/hour | | -------------------- | ---- | ------ | ------ | --------- | | Small | 1 | 4 GB | 64 GB | \$0.10 | | Medium | 2 | 8 GB | 64 GB | \$0.20 | | Large | 4 | 16 GB | 64 GB | \$0.40 | | Ultra | 8 | 32 GB | 64 GB | \$0.80 | | Hyper | 16 | 64 GB | 128 GB | \$1.60 | | Big Guy (enterprise) | 16 | 128 GB | 256 GB | \$3.20 | Large is the default. You pay while a machine is awake, plus a two-minute wind-down when it goes to sleep. A sleeping machine costs nothing, but waking it to extend its uptime for ports, desktop, file viewer will incur a charge. See [machines](/machines). ## Enterprise Need more than Capy Max's \$1,000/month in credits, custom pricing, or invoiced billing with net terms? We set up a custom plan. Everything else works the same as on standard plans. [Contact us →](https://cal.com/team/capy/enterprise) ## FAQ Subscribe to monthly Capy Lite with a verified card and your first 7 days cost \$1, then \$20/mo unless you cancel. One intro per card and per organization, ever. No. Every plan includes unlimited members with no per-seat price, so inviting someone never creates a member charge. Active runs stop with a visible error in the thread. Add balance or let auto-reload top up, then retry the thread. Nothing resumes on its own. Yes, from the billing page. Every plan change applies immediately and may include a prorated charge. Canceling instead keeps the current plan active through the end of its billing cycle. Annual plans bill upfront for 12 months at a 20% discount. Credits are still granted monthly. No, you can add balance directly (minimum \$5). A plan grants credits monthly at a discount. # Projects Source: https://docs.capy.ai/projects A project is the context group your threads run in. It owns the repository catalog (which repos a machine gets, and each repo's base branch), the [dev environment](/environment), [environment variables](/secrets), [snapshots](/environment#snapshots), [automations](/automations), and integration routing like Linear team mappings. Project threads spawn with project repos cloned automatically. Every enabled member of the organization can read every non-personal thread in every project. [Service users](/admin/members#service-users), however, carry their own project access (all projects, or an explicit list). Repository access is gated through GitHub: a human's clones and pushes use their own GitHub access and a service user's run through the Capy GitHub App installation. ## Repositories A project carries an ordered list of repositories, each with a base branch: the branch machines check out and PRs target by default. You can also create repoless projects for research, planning, and general computer-use work. Connecting the GitHub App grants Capy access to clone and use repos for projects. You pick what the App may reach in GitHub's installer, then pick what the project contains in Capy's repository picker. ## Identity Each project has a name, an icon/emoji, and a short uppercase code of up to four letters that prefixes thread references: `CAPY-42` is thread 42 of the project with code `CAPY`. ## Working across projects You can view threads from every project in the [threads page](/threads). [Folders and tags](/threads#folders) are organization-wide and can mix threads from different projects freely. [Volumes](/volumes) share instructions, skills, and files across threads outside git. For cross-project rules, see [Instructions for every project](/instructions#instructions-for-every-project). # Pull requests Source: https://docs.capy.ai/pull-requests Capy ships code by opening pull requests, and subscribing to CI results, review feedback, and merge results. ## Capy opens the PR Ask for a PR and the agent commits, pushes, and opens it with one tool. When its checkout sits on the branch the PR targets (`main`, `master`, `develop`, a `release/*` branch, or whatever base you configured), it mints a fresh `capy/` branch at publish time, named from the PR title, and pushes there. Your main branch never carries agent work, even when the agent was editing a checkout of it directly. ## What wakes the thread after opening Once the PR exists, the thread will automatically wake up to certain events: * **CI result:** A failing check wakes the thread; it reads the failing job, fixes, re-pushes, and the rerun's result comes back the same way. Passing checks arrive as one consolidated result once they all finish. * **Automatic Capy reviews:** If auto-review is enabled for the repository, pushes get review rounds and the verdict lands in the thread that owns the PR; see [PR reviews](/review). * **Human feedback:** Review comments and PR comments from people wake the thread; it triages the feedback and replies. Bot comments are ignored by default; your org's feedback settings can allowlist specific bots. * **The merge:** Merging wakes the thread so it can wrap up, and a thread you marked ready for review settles to idle on its own when a merge lands and no PR anywhere in its task tree is still open. Only a merge triggers the move to idle; a PR closed without merging doesn't settle the thread by itself, but it doesn't block a later merge from settling it either. ## Stacked PRs When new work depends on a PR that hasn't merged yet, the agent opens the next PR with the previous PR's branch as its base. Each PR in the stack then shows only its own diff, so reviewers see one change at a time instead of an accumulating pile. ```text theme={null} Open a PR for the schema migration first, then stack the API change on top of it as a second PR. ``` ## Which thread owns a PR Ownership follows the most recent push. The thread that opened a PR owns it; when another thread later pushes to the same branch (a side thread finishing stalled work, a task picking up a review round), that thread takes over, and follow-up events like review verdicts and the merge route to it. "Whose PR is this" is always "who last shipped code on the branch." # Quickstart Source: https://docs.capy.ai/quickstart This walkthrough takes you from signup to a merged pull request. You'll sign in, start a thread, and review Capy's completed work. Go to [capy.ai](https://capy.ai) and sign in. Install the Capy GitHub App, choose which repositories it can access, and pick the repos for your first project. Pick a model and send your first prompt to Capy. Optionally connect external subscriptions like Codex or Supergrok in [model settings](https://capy.ai/settings/models). Capy boots a machine with your repos checked out and gets to work. Use one thread per task. The thread shows Capy's progress live: its todo list in the thread pane, the commands it runs, the diff as it grows. The thread's status tells you what it's doing: **active** while it works, **waiting** when it's parked on something external, **needs attention** when it's blocked on your input. The thread will keep running until Capy is done. Since Capy runs in the cloud, you don't have to keep the tab open. Send follow-ups anytime: by default a new message interrupts Capy immediately, or you can queue it to deliver when the current work finishes. See [working in threads](/threads) for the delivery model. When the change is done and verified, Capy commits, pushes, and opens the PR itself, then reports back with a link. Capy automatically subscribes to PR events: CI results, review feedback, and the merge all reach the thread automatically. A CI check wakes the thread on the first failed result, and Capy fixes the failure and pushes again. If [automatic review](/review) is enabled for the repository, the review agent reads the diff and posts findings inline on GitHub, and the thread receives the verdict and triages the findings itself. Capy will stop and let you know when it is done and is ready for your review, or is genuinely blocked. The PR diff, the checks, and the review findings can all be found on the dedicated PR page or in the thread's side pane, or on GitHub if you prefer. Comment on the PR if you want changes and Capy will respond. After merging, Capy will acknowledge it and stop or continue the stack if there are more PRs to tackle. # Reviews Source: https://docs.capy.ai/review Capy reviews pull requests with a dedicated review agent. It reads the diff in a real checkout of your repository at the PR's exact head, and posts findings as inline GitHub comments. Every finding is also recorded in Capy and relayed to responsible agent threads. ## Findings Each finding names a failure scenario and carries a category, a severity, a confidence, and a file and line. | Category | What it covers | | ------------------- | -------------------------------------------------------- | | **Bug** | Logic errors, incorrect behavior, crashes | | **Risk** | Security issues, race conditions, data loss potential | | **Maintainability** | Code clarity, naming, documentation gaps | | **Refactor** | Structural improvements, duplication, pattern violations | | Severity | Meaning | | ---------- | -------------------------------------------- | | **High** | Correctness, security, or data-loss defects | | **Medium** | Maintainability or correctness concerns | | **Low** | Style, minor improvements, and nice-to-haves | **Confirmed** means the diff plus traced context proves the defect, **investigate** means a concrete risk with a verification target. A review can also leave **notes**: anchored observations that name no failure, like a "this looks wrong but is correct, because X" verdict. ## Starting a review 1. **Automatically per repository.** Set each repository's mode in [Settings → Review](https://capy.ai/settings/review): **Off**, **Once** (first review when a PR opens), or **Every push**. Enabling reviews for a repository takes its review billing for your org. 2. **Manually from the dashboard.** Open a pull request in Capy and start a review from its page. 3. **From a PR comment.** Comment `@capy review` on the pull request. Trailing text becomes the round's extra instructions: `@capy review focus on the migration`. 4. **From the API.** `POST /api/v1/reviews` starts a round for a repo and PR number, see the [API reference](/api-reference/overview). A push while a review is running starts a fresh round against the new head. When a review runs on a PR a Capy thread opened, the thread receives the verdict and triages the findings itself. ## What posts to GitHub Findings at or above the repository's posting threshold post as inline review comments. The default threshold is **medium**; repository settings can change it, toggle whether **investigate** findings post, and opt notes in. Re-reviews don't flag the same issue twice. When a finding is marked **resolved** or **irrelevant**, Capy resolves the corresponding GitHub thread. ## Triage Each finding has a triage status. | Status | Meaning | | -------------- | --------------------------------- | | **Open** | Active issue that needs attention | | **Resolved** | The issue has been fixed | | **Irrelevant** | False positive or not applicable | When a Capy thread owns the pull request, it triages after each review: it marks false positives **irrelevant**, confirms fixed issues as **resolved**, and fixes high-severity issues before reporting the PR ready. You can override any triage decision from the dashboard. ## Re-reviews Re-reviews are incremental: only code changed since the last reviewed head is in scope, with the whole PR as context. Open findings carry forward, and the agent marks the ones it verifies are gone as resolved. Findings you marked **irrelevant** are never re-flagged; a **resolved** finding whose fix didn't hold comes back as a fresh finding. ## Steering reviews The review agent reads your repository's `AGENTS.md` files and enforces the conventions written there as findings, on the same footing as logic bugs. Write the rules you want enforced, or the patterns you never want flagged: ```markdown theme={null} ## Reviews - Snapshot ID changes are normal workflow; do not flag them - Always check for SQL injection in new queries - Flag any direct database access outside the repository layer ``` Changes take effect on the next review. The deprecated `.capy/REVIEW.md` per-agent file is no longer read. # Secrets Source: https://docs.capy.ai/secrets Environment variables are workload input for the [machine](/machines), and Capy never injects a value into its prompts or tool arguments. You set values in the app; Capy encrypts them at rest, injects them into the machine's process environment at boot, and shows the agent only the variable names and whether each has a value. No tool, prompt, list API, or log carries a value, and no tool can set one. The limit: the machine is the agent's execution environment, so code the agent runs can read anything on the machine, including your variables. Scope what you hand over accordingly. ## The three scopes | Scope | Who sets it | Where it lands | On name collision | | ------------ | --------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------- | | **Shared** | Any project member, on the project's Environment variables page | Every machine in the project (threads, tasks, snapshot builds, reviews) | Loses to both | | **Personal** | You, on the same page's Personal tab | Machines for threads you start; never reviews | Beats shared | | **Thread** | You, from the composer's environment variables dialog | One running thread and its tasks; never reviews | Beats both | Personal variables are for credentials that belong to you alone: your staging login, your API sandbox key. Thread variables are for handing a value to one running thread without publishing it to the project: the agent asks for a `DATABASE_URL` mid-run, you send it, it lands on the thread's machines immediately. One caveat on personal values: a machine isn't single-user. If you share a thread, whoever drives it can read your personal values on that machine, because they're in the environment like everything else. ## What the model sees, and what it can leak * The model sees variable names and configured-state. Values never enter the prompt, tool arguments, or anything Capy authors. * Values exist in plaintext exactly where they must: in your write request, briefly in Capy's worker while assembling the machine's environment file, and on the machine itself. * **There is no output masking.** The agent runs arbitrary commands, and a command that prints a value (`printenv`, a stack trace, a verbose CLI) puts that value into tool output, and tool output flows into the thread transcript like any other text. So the real threat model is that any secret on the machine can end up in the transcript if a command prints it. Give the agent scoped, revocable credentials for the job at hand, not your production root keys. ## How values reach a machine Values are encrypted at rest (each value sealed under a KMS-managed key, bound to its exact project, scope, and name) and decrypted server-side only when a specific machine is being prepared. They're written to a permission-restricted file outside your repository trees, excluded from diffs and snapshots, and sourced into every shell the agent runs, so test runners, SDKs, and arbitrary commands see them as ordinary environment variables. When values take effect: * Values are written when a machine boots. Editing a shared or personal value affects machines at their next boot; to get a value onto a running thread's machines right now, send it through the composer's environment variables dialog, which pushes immediately. * A running process keeps the environment it launched with until something restarts it. Reading values back: the app reveals one value at a time to an authorized member (the eye toggle on the variables page). Lists show names and metadata only, and thread variables are write-only: you typed the value moments ago; resend it if needed. ## Snapshots and scrubbing [Snapshot builds](/environment#snapshots) receive shared project values so your `initialize` script can authenticate private registries. Before capturing the image, Capy deletes the environment file and every platform-managed sensitive path, then verifies each one is gone. A build that can't prove the scrub fails rather than baking a secret into a reusable image. What the scrub can't prove absent is your own script's behavior: an `initialize` that copies a value into a file in the workspace bakes that file into the snapshot. Keep secrets in variables; don't write them to disk. ## What never lands on a machine * GitHub credentials. Every git operation routes through Capy's proxy, which injects credentials server-side; the machine holds only a bearer scoped to itself. * Capy's own platform secrets (database, KMS, provider keys). The machine has no credential that can read any secret source. * Other tenants' anything. If you paste a provider token in as a plain project variable, that's allowed: it's a workload-input decision, and it travels with exactly the exposure this page describes. # Skills Source: https://docs.capy.ai/skills A skill is a folder in a repository or [volume](/volumes) that defines a reusable workflow. Capy sees available skill names and descriptions, then reads the full instructions when relevant. ## Where skills live and how they load Repository skills live under `.agents/skills/` (preferred) or `.claude/skills/` (supported for compatibility), one folder per skill, each with a `SKILL.md` at its root. When both roots define the same skill name, `.agents/skills/` wins; across a multi-repo project, the first repo in the project's repo order owns a contested name. Volume skills live at `skills//SKILL.md` outside git. Personal and Organization volumes share workflows across projects; Project and Project + personal restrict them to one project. See [Volumes](/volumes#reusable-skills) for setup. Loading is read-based: the agent's context always carries the list of available skills (each skill's name, description, and the on-machine path of its `SKILL.md`), and the agent opens the file with its ordinary read tools when the description matches the task. There is no activation step and no permission gate; a skill is exactly as discoverable as its description makes it. Skill lists share one context budget with your `AGENTS.md` instructions. When the budget overflows, skills degrade without disappearing: descriptions shorten, then empty out, each state with a visible marker, but every skill's name and location always renders, so the agent can still find and read it. ## The SKILL.md contract Repository skills require `name` and `description` in frontmatter. Volume skills use the folder name and accept missing fields, but descriptions help Capy select the workflow. Include both fields for portability: ```markdown theme={null} --- name: deploy description: Deploy the application to staging or production. Use when asked to deploy, release, or push to an environment. --- # Deploy skill ## Steps 1. Run `scripts/deploy.sh ` 2. Verify the deployment at the environment URL 3. Report success or failure ## When not to use - Local development or testing; run the dev server instead. ``` Write the description as the trigger: it's the only part the agent sees before deciding to read the file, so "Use when asked to deploy, release, or push to an environment" beats "Deployment documentation." Beside `SKILL.md`, a skill folder can carry whatever the workflow needs, conventionally `scripts/` (executables the instructions invoke), `references/` (longer material the instructions point into), and `assets/`: ## Skill or AGENTS.md? Root [`AGENTS.md`](/instructions) supplies standing rules; skills load on demand for specific workflows. Both can live in repositories or volumes. Repository skills version with the code. Volume skills persist outside git for threads in their scope. Ask Capy to create a skill by describing the workflow and its destination. # Support Source: https://docs.capy.ai/support For the fastest response, open your user menu in [Capy](https://capy.ai) and select **Contact us** to start a chat with our support team. If you cannot access the app or prefer email, contact [support@capy.ai](mailto:support@capy.ai). # Tasks Source: https://docs.capy.ai/tasks A task is a full child agent your thread's agent spawns to work in parallel: its own conversation, its own tools, and usually its own machine. A task can run commands, open PRs, and spawn tasks of its own, up to three levels deep. Tasks are addressed by position: the first is Task 1, and Task 1's third task is Task 1.3. You don't create tasks directly; you ask the agent to split the work, and it drafts, starts, and coordinates them. A task starts as an editable draft (a title plus the full prompt its agent will run with) and does nothing until started, so the agent can lay out a whole plan, refine it, and launch pieces in the right order. ## Tasks work in isolation **A task starts from its prompt alone.** It hasn't seen your thread's history, your earlier corrections, or the files the parent explored: everything it needs must be in the prompt it's given. And the parent isn't fed each step as it happens: it gets the final summary automatically and reads interim progress only when it asks. Tasks are cheap parallelism precisely because they don't drag the whole conversation context with them. ## Shared vs fresh machines A task either shares the parent's machine or gets a fresh one, and the split follows writes: * **Shared**: the task works on the parent's machine and sees its working tree exactly as it stands, uncommitted changes included. Right for tasks that only read: exploring, auditing, summarizing. * **Fresh**: the task boots its own machine with a clean checkout from the upstream branch. Right for tasks that write: each writer gets an isolated tree, so two tasks editing code can't collide with each other or with the parent. Capy must choose one explicitly whenever it starts a task. The start result names the placement and machine id, so a mismatch is visible before the child reports missing files. ## Parallel vs stacked * **Parallel** tasks run simultaneously and must touch disjoint parts of the codebase: different packages, different files. Each ships its own PR. * **Stacked** tasks depend on each other or touch overlapping files: Task 1 runs and ships its PR, then Task 2 starts from Task 1's PR branch and builds on top. Slower, but conflict-free where parallel would collide. When in doubt, stack. A merge conflict between two parallel tasks costs more than the waiting. ## Task lifecycle | Status | Meaning | | --------------------- | --------------------------------------------------------------------- | | `draft` | Created but not started; the prompt is still editable. | | `working` / `waiting` | Running, or parked on an external event like CI. | | `idle` | At rest, including after a stop. A stopped task is just an idle task. | | `done` | Delivered a final result to the parent. | | `failed` | Ended in an unrecoverable error. | ## How results come back A task reports to its parent through explicit messages: * A task that finishes delivers its final summary to the parent, which wakes and acts on it. * A task that gets blocked (it needs a decision, access, or credentials) surfaces its actual question to the parent as a notification. The parent can answer it, answer with your help, or escalate to you. * Interim progress messages don't wake the parent; it reads them on demand when it checks on the task. ## Reviewing and shipping task work A completed task's work sits on its machine until someone ships it. The parent reviews by inspecting the diff on the task's machine, then either opens a PR from the task's branch, patches small problems itself, or sends the task targeted feedback and lets it revise. You can direct any of this: "check Task 2's diff before opening the PR" or "tell Task 1 the API changed and have it rebase." ## Models per task Each task can run on its own model: a cheaper model for mechanical work, a stronger one for a gnarly refactor, or a subscription-billed seat (like a connected Codex subscription). By default tasks inherit the thread's model; ask for a specific split when you want one: "run the test-writing tasks on the cheapest reasonable model." ## Coordinating across threads Tasks stay inside one thread. For genuinely separate workstreams, the agent can also create sibling threads (each a root-level workspace with its own agent, machines, tasks, and PRs) and message them. A sibling thread answers to you in its own thread, and a coordinating agent has to explicitly read a thread's state to learn anything. Use sibling threads only when you're deliberately running one controlling thread over several long-lived workstreams; ordinary parallel work belongs in tasks. ## Prompts that orchestrate well Parallel split across disjoint files: ```text theme={null} Migrate our API handlers from Express to Fastify. Split it into parallel tasks by directory (one task each for routes/auth, routes/billing, and routes/webhooks) on fresh machines, each shipping its own PR. Give every task the migrated example in routes/health as the pattern to follow, and require passing tests before it reports done. ``` Stacked feature where the pieces overlap: ```text theme={null} Add org-level audit logging. Stack it: Task 1 builds the audit_log table, migration, and write API, and ships a PR. When that PR is up, start Task 2 from Task 1's branch to wire logging into the settings and members endpoints, as a second PR on top. Review Task 1's diff before starting Task 2. ``` Cheap parallel audit, shared machine: ```text theme={null} Before we cut the release: spawn three read-only tasks on your machine to audit the diff since v2.3: one for breaking API changes, one for missing migrations, one for untested code paths. Have each report findings with file references, then give me the combined list ranked by risk. ``` # Threads Source: https://docs.capy.ai/threads A thread is a durable conversation with one Capy agent, stored on Capy's servers: close the laptop mid-run and the agent keeps working, and the thread wakes on its own when CI finishes, a review lands, or a PR merges. Use one thread per piece of work, and message it the way you'd message a colleague; it holds the full history of the conversation, the machines, the tasks, and the PRs that came out of it. ## Sending messages Every message you send carries a delivery mode that decides how it reaches a busy agent: | Mode | What happens | | ----------------------- | ----------------------------------------------------------------------------------- | | **Interrupt** (default) | Stops the agent's current generation; your message becomes the next thing it reads. | | **Queue** | Waits until the current work finishes, then delivers. | | **Steer** | Injects into the current work at the agent's next step, without stopping anything. | Enter sends as interrupt by default, Cmd/Ctrl+Enter sends as queue, and Option/Alt+Enter sends as steer. All three gestures are configurable in Settings → General under Input, and each gesture has one stable meaning. When the thread is at rest there's nothing to interrupt, queue behind, or steer, so every send just delivers immediately. ## Thread statuses A thread's status is derived from what the agent and its tasks are doing, in this precedence: active beats error, error beats needs-attention, and so on down to idle. The sidebar and threads page fold statuses into five buckets: | Bucket | Meaning | | -------------------- | ---------------------------------------------------------------------------------- | | **Needs attention** | The agent is blocked on your decision, access, or credentials, or it hit an error. | | **Ready for review** | The agent finished work and handed it to you for review. | | **Active** | The agent or any of its tasks is working right now. | | **Waiting** | An external event owns the next move: CI, a callback, a long-running operation. | | **Idle** | At rest. Nothing happens until someone messages it. | Skim the needs-attention and ready-for-review buckets first; those are the threads waiting on you. Active and waiting threads don't need you yet. ## Marking status by hand You can move a resting thread between buckets yourself: the Status submenu in any thread's context menu offers **Ready for review**, **Needs attention**, and **Idle**. Marks are the human's word until someone speaks again: your next message or the agent's next turn-ending reply overwrites them, and you can't mark a thread that's actively working. A thread sitting in ready-for-review settles to idle on its own when a merge lands and no PR anywhere in its task tree remains open. ## Archive is dormant Archiving removes a thread from your lists and puts it fully to sleep: no CI result, task completion, or reminder wakes it. Events that arrive meanwhile still land as pending context (nothing is lost), but nothing runs. Archived threads can still be revived with a human message. Sending to an archived thread unarchives it and wakes the agent with everything that accumulated while it slept. Archive is for threads you're done looking at. To mark work finished without hiding it, mark the thread idle instead. ## Todos are the agent's visible plan For multi-step work the agent keeps a todo list: its working plan, updated as it goes. Items move through `pending`, `in_progress`, `completed`, `cancelled`, and `deferred`, with at most one item in progress at a time. ## Voice input The microphone button in the composer dictates straight into the message, live, at your cursor; you can keep typing around it while you talk. Accept commits the transcript, discard throws it away, and a recording auto-accepts at the 10-minute cap so nothing you said is lost. Undo reverts the dictation as one edit. ## Attachments Attach images and files to any message from the composer; they land on the agent's machine, where it reads them with its ordinary tools. ## The command menu Cmd+K opens the command menu from anywhere: * Typing searches your threads full-text: titles, thread codes, and message content. * It also finds pull requests, automations, and settings pages as you type. * At rest it lists your most relevant threads in the same priority order as the sidebar, plus navigation and theme actions. * Tab hands off to a full new-thread composer seeded with whatever you typed, so a thought becomes a thread without leaving the keyboard. Every keybinding here and in the composer is configurable at Settings → Shortcuts. ## Folders Folders are flat, org-level groupings of threads. A folder is either **private** (only you see and edit it) or **shared** (every member of your organization sees and edits it), and a thread can sit in any number of folders at once. Folder membership changes nothing about the thread itself, and the All threads, status, and search views stay complete: filing a thread adds a view, it never removes the thread from ordinary results. **Pinned** is a reserved private folder that always renders first. Pinning a thread is just membership in that folder, so pins reorder like any other folder and unpinning is removal. Pinning an already-pinned thread again moves it to the front. Folder and tag memberships survive archiving, so an unarchived thread comes back exactly where you filed it. ## Tags Tags are organization-wide labels: one shared vocabulary across every project, each tag with a name and color. A thread carries up to 20 tags. Tags filter and decorate thread lists; they don't place threads anywhere, so use folders for "where does this live" and tags for "what kind of thing is this". ## Search Search is full-text over titles, thread codes, task titles, and message content, both what you wrote and what the agent replied. That matters because most thread titles are auto-generated: searching for the error string you pasted three weeks ago finds the thread even if its title never mentioned it. Search from Cmd+K anywhere, or from the search box on the threads page. ## Titles Every thread titles itself from your first message: a compact phrase of at most 50 characters, generated while the agent starts working. There are two ways you can manually change a thread's title, both in the thread's context menu: * **Rename** sets your own title. Explicit titles stick; regeneration and auto-titling never overwrite a name you typed. * **Regenerate** asks for a fresh title from the conversation so far, and clears the explicit-title mark so the thread is auto-titled again. # Troubleshooting Source: https://docs.capy.ai/troubleshooting Each entry names the symptom, what actually happened, and the fix. The owning page has the full mechanics. ## My machine died mid-thread An expired or reclaimed sandbox is replaced on the next use using the project's [dev environment](/environment). Pushed repository changes and files saved in [volumes](/volumes) survive [machine replacement](/machines); unpushed checkout edits and scratch files don't. Ask Capy to reconstruct lost work from the thread history. ## My files are gone after the machine slept Sleep persists the filesystem, so files survive sleep; what dies are processes, memory, and connections, because resume is a cold boot. If a dev server was running before sleep, it isn't after; `startup` scripts re-run on wake to bring services back. A machine asleep past 2 days moves to colder storage (still resumable); at 30 idle days it's deleted, and unpushed work goes with it. Details on [machines](/machines#sleep). ## My setup script failed The machine still comes up: setup failure degrades, it doesn't block. The thread gets a workspace warning naming the failing repo and phase, and the agent usually repairs the workspace by hand for that run. Fix the script itself in the [dev environment](/environment) so the next machine doesn't need repairing, and check the phase: `initialize` runs once per fresh machine, `update_after_checkout` when the checkout moves, `startup` on every boot including wake. ## My snapshot build keeps failing Snapshot builds never gate work: machines fall back to full setup, so a failing build only costs boot speed. Read the build's log from the [snapshots](/environment#snapshots) surface or ask Capy for it; the usual cause is a setup script that isn't idempotent or depends on something transient. Fix the script, then trigger a rebuild or wait for the next content change. ## A run stopped with an insufficient-balance error The run hit the balance gate and stopped with a durable error. Nothing polls your balance, and nothing resumes by itself. Add balance (or let [auto-reload](/admin/billing#auto-reload) do it), then message the thread or hit retry; it picks up from its recorded history. Auto-reload skips when a reload would exceed its monthly cap, and fails when the payment method declines. See [models & pricing](/models-and-pricing#when-your-balance-runs-out). ## CI failed but my thread never reacted Almost always: the PR wasn't opened by the thread. Only a PR the agent opened through its own machinery subscribes the thread to that PR's CI, review, and merge events; a PR someone opened by hand with `gh pr create` delivers none of that. Ask the agent to take over the branch and open the PR itself, or relay the CI result manually. See [pull requests](/pull-requests). ## The thread has been "waiting" for ages Waiting means an external event owns the next move: CI still running, a review round in flight, a timer, a task. Look at the newest thread events to see which. If the awaited thing already happened out-of-band (you merged manually, CI was cancelled), just message the thread and say so; a human message always wakes it. ## Capy doesn't respond in my archived thread Archive is dormant by design: every event still lands as pending context, but nothing wakes the thread except a message from a person, which unarchives it and resumes with everything that accumulated. Unarchive alone reopens the lists without starting a run. See [working in threads](/threads#archive-is-dormant). ## The exposed port URL stopped working Exposed URLs serve a live process on an awake machine. When the machine sleeps, the process is gone (cold-boot rule) even after the machine wakes. Ask the agent to restart the server, or put it in `startup` so every wake brings it back. ## An MCP server's tools aren't available Check scope and credentials in order: the server must be enabled for the project, and it needs a live connection. Personal connections beat shared ones, and an expired OAuth grant flips the server to needs-auth, which removes its tools until someone reconnects. Reviews never see MCP tools regardless; only the capy agent uses them. See [MCP](/integrations/mcp). ## Still stuck [Contact support](/support) and include the thread reference (like `CAPY-42`), which appears in the thread's header. # Volumes Source: https://docs.capy.ai/volumes Volumes store instructions, skills, and shared files outside git for reuse across threads. ## Scopes | Scope | Used by | | ------------------ | -------------------------------------------------------- | | Organization | Threads across the organization's projects | | Personal | Your threads across projects in the current organization | | Project | Threads in one project | | Project + personal | Your threads in one project | | Automation\* | Runs of one automation | \* Not used for instructions or skills; holds automation state and persisted work between runs. Organization and Project volumes are shared with the organization's members. Personal and Project + personal volumes aren't browsable by other members. All scopes stay within one organization. ## Global instructions A volume's root `AGENTS.md` supplies instructions to threads in its scope. > Add “Use conventional-commit PR titles” to AGENTS.md in my personal volume. Preserve existing instructions. See [Instructions](/instructions) for repository and file-specific rules. ## Reusable skills Volume [skills](/skills) live at `skills//SKILL.md` and load on demand. > Create a release-checklist skill in our project volume from the release procedure we agreed on. ## Save and update files > Save the approved launch checklist in our project volume for future threads. Capy can update or remove saved files on request. Changes save automatically as work completes, without a git commit or separate publish step. Checkout and scratch files aren't included. ## Browse files and history Open **More → Volumes**, or **Volumes** if pinned in the sidebar. **History** lists saved versions and their originating threads. The viewer is read-only; ask Capy to edit files or recover a retained version. ## Storage limits Limits are 100 MiB per file, 2 GiB per saved volume version, and 10 GiB per organization. History doesn't retain every intermediate version. # Welcome Source: https://docs.capy.ai/welcome Capy is a cloud coding agent for shipping real software. Start a thread from the web app, Slack, Linear, or the API. Capy boots a machine with your repos, makes the change, opens the PR, and handles CI and review feedback on its own. ## Start here Go from signup to your first merged PR Compare models, plans, and rates ## What you can do with Capy Describe a change in a thread and get a PR back, CI handled Split large work across subagents on independent or shared VMs A dedicated review agent posts findings on your PRs Run prompts on a schedule or on platform events Setup scripts, snapshots, and secrets so the agent can run your code GitHub, Slack, Linear, Vercel, and MCP servers