# Agent-assisted setup Source: https://docs.aient.ai/agent-commerce Use an AI assistant to prepare Aient setup safely: choose a plan, gather repository details, connect approved integrations, and install telemetry without bypassing human consent. # Agent-assisted setup An AI assistant can help you prepare Aient setup, but it should not bypass payment, integration consent, or code review. Use it to gather the right details, follow the docs, and draft the changes you review. ## What an assistant can do | Task | What to ask for | | - | - | | Choose a setup path | Compare the server, browser, and source map guides for your stack. | | Prepare repository details | Identify the service name, deployment environment, and repository to monitor first. | | Install telemetry | Apply the [server](./quickstart/server) or [browser](./quickstart/browser) quickstart and keep release metadata aligned with deployments. | | Add source map uploads | Wire the [source map upload](./source-maps/upload) command into CI after the frontend build. | | Summarize next actions | Produce a checklist for payment, GitHub, Slack, telemetry keys, and verification. | ## Human approvals Aient keeps high-trust actions behind a human or workspace admin: * choosing or changing a paid plan * approving payment in Stripe * installing the GitHub App * connecting Slack and selecting channels * creating or rotating server-side credentials * reviewing and merging remediation PRs If an assistant reaches one of those steps, it should hand you the link or checklist and wait for confirmation. ## Information to prepare | Detail | Example | | - | - | | Organisation name | `Acme Engineering` | | Repository | `acme/web` | | First service name | `checkout-api` | | Environment | `prod`, `staging`, or `dev` | | Runtime | Node.js server, browser app, or both | | Release metadata | commit SHA, branch, deployment environment | | Slack channel | the channel where Aient should post problem updates | Stable names matter. Use the same `serviceName` in telemetry and source map uploads so Aient can connect stack traces to the right release. ## Safe credential handling * Use publishable environment keys in application telemetry. * Store source map upload keys only in CI or other server-side secret stores. * Do not paste private keys, Stripe secrets, GitHub tokens, or Slack tokens into a general-purpose assistant. * Ask the assistant to make a PR or patch, then review the diff before merging. ## Verify setup After the assistant makes changes: 1. Deploy a small change with release metadata. 2. Confirm traces and logs arrive in Aient. 3. Trigger or inspect a known error in a non-production environment first when possible. 4. Upload source maps for the same commit SHA. 5. Check that stack traces resolve to readable files and line numbers. ## Related guides * [Server telemetry quickstart](./quickstart/server) * [Browser telemetry quickstart](./quickstart/browser) * [Upload source maps](./source-maps/upload) * [OpenTelemetry endpoints](./reference/otlp-endpoints) # Agentic x402 onboarding Source: https://docs.aient.ai/agentic-x402 How autonomous agents discover Aient through x402 and Coinbase Bazaar, create a wallet-backed organisation, ingest telemetry, and request AI remediation. # Agentic x402 onboarding Aient exposes paid, agent-native HTTP routes through x402. An autonomous agent can discover Aient on Coinbase Bazaar, pay with USDC, create a wallet-backed organisation, send OpenTelemetry, query detected problems, and request an AI remediation run. Use this path when the buyer is an agent or wallet principal. Use [Agent-assisted setup](./agent-commerce) when an assistant helps a human or team prepare a normal Aient setup without bypassing payment, integration consent, or code review. ## Flow 1. Discover Aient on Bazaar or from route metadata. 2. Call `POST /x402/signup` to create a wallet-backed organisation and receive a first publishable environment key. 3. Send OpenTelemetry to Aient with that key. 4. Query service health, logs, traces, or detected problems. 5. Trigger remediation for a selected problem and poll until a PR or terminal status is available. ## Core routes | Route | Purpose | | - | - | | `POST /x402/signup` | Pay once to create a wallet-backed organisation and first environment key. | | `POST /x402/onboard` | Start the guided onboarding session when GitHub approval or instrumentation is needed. | | `POST /x402/diagnose` | Describe an incident and get the cheapest next action toward a real diagnosis or remediation. | | `POST /x402/instrumentation/pr` | Ask Aient to open an instrumentation PR for a repository and service. | | `GET /x402/problems` | List Aient-detected problems for the wallet-backed organisation. | | `GET /x402/problems/{id}` | Read one problem with stack traces, sample trace ids, and remediation state. | | `POST /x402/problems/{id}/remediations` | Start an autonomous remediation run for a detected problem. | | `GET /x402/remediations/{id}` | Poll remediation status and retrieve the PR URL when it is ready. | | `POST /x402/resolve` | Shortcut route for the guided onboarding flow to request a fix once telemetry is ready. | Read routes are priced low enough for agents to inspect state repeatedly. Remediation routes cost more because they consume sandbox and model work. ## Session rules `POST /x402/onboard` returns a session token once. Store it. Free polling routes use: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Authorization: Bearer ``` Paid routes still require x402 payment. A session token is not a payment bypass. When a response includes `next_actions`, follow the first action unless your agent has a better operator instruction. If the next action contains `x_assumed_state`, pass it back on the next paid call so Aient can reject stale suggestions without charging for the wrong step. ## Human approval Some steps still require a human or admin: * GitHub App installation * repository selection * destructive or high-risk changes * reviewing and merging remediation PRs When approval is required, Aient returns a link and a waiting state. The agent should hand that link to the operator, then poll the session until the state advances. ## Remediation output Remediation starts asynchronously. The first response returns a remediation id, thread URL, and status. Poll the remediation route until the result includes a PR URL or a terminal failure. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "remediationId": "rem_01HZ...", "status": "awaiting-review", "threadUrl": "https://aient.ai/threads/thr_01HZ...", "prUrl": "https://github.com/org/repo/pull/42" } ``` The PR is the review boundary. Aient does not ask agents to merge code automatically. # Use Aient docs from AI assistants and coding agents Source: https://docs.aient.ai/ai-tools Point Cursor, Claude Code, and other AI tools at Aient docs through generated Markdown, llms.txt, and skill.md so agents instrument apps from the same source. # AI tools Mintlify exposes documentation content as Markdown for agents, hosts `llms.txt`, and can publish a `skill.md` capability file. Use those generated surfaces so AI tools answer from the same docs users read. ## Recommended use * Point coding agents at this docs site before instrumenting an app. * Use the webhook event pages as schema context for consumer implementations. * Prefer the SDK quickstarts over raw OTLP setup unless you need a custom OpenTelemetry pipeline. * Use the [Aient CLI](./ai-tools/cli) to run a local workspace in an isolated Aient sandbox. * Use [Agentic x402 onboarding](./agentic-x402) when an autonomous agent needs to discover Aient, pay with x402, and request remediation through wallet-backed routes. * Use [Agent-assisted setup](./agent-commerce) when an assistant helps prepare setup for a human or team. * Use [Kubernetes connections](./ai-tools/kubernetes-connections) to let Threads read one of your clusters with `kubectl`, read-only, through a connector you install. ## Discover the MCP server Aient's hosted MCP server is listed in the official MCP Registry as `ai.aient/mcp` and is reachable at `https://aient.ai/mcp`. Use registry metadata when your assistant supports registry-based installation. Otherwise, configure the remote MCP URL directly. Request read scope for context lookup, and request write scope only when the assistant needs to report onboarding progress, retry setup steps, or control remediation workflows. ## Reopen a resolved problem If new production evidence changes the conclusion for a resolved problem, an assistant with MCP write scope can call `reopen_problem`. Include a fresh request ID and a short reason grounded in that evidence. Then re-read it with `get_problem` before making a separate remediation request. Reopening keeps the existing evidence and remediation history intact. It is for a new conclusion, not for undoing an accidental manual resolution. `reopen_problem` is available through the OAuth MCP server, not the wallet-backed `/mcp-x402` endpoint. # Aient CLI Source: https://docs.aient.ai/ai-tools/cli Authenticate per project, offload a local workspace, and operate isolated Aient sandboxes. # Aient CLI The `aient` command runs local development work in isolated remote sandboxes. The current release is available as a static binary for macOS and Linux on Intel and Arm. Current release: `0.11.41` > [card] [Authentication and projects](/ai-tools/cli/authentication) > > Keep separate customer sessions and bind each checkout to the intended Aient profile. > [card] [Run and synchronize](/ai-tools/cli/run-and-sync) > > Run a local Git worktree remotely or synchronize its exact state into a retained sandbox. > [card] [Retained sandboxes](/ai-tools/cli/retained-sandboxes) > > Create, inspect, reuse, enter, and delete sandboxes. > [card] [Executions and files](/ai-tools/cli/executions-and-files) > > Observe live commands and transfer explicit files. ## Install and verify Download the current release from the [Aient CLI release repository](https://github.com/aient-ai/aient-cli/releases/latest). Follow the repository's [installation and verification guide](https://github.com/aient-ai/aient-cli#install-on-macos-or-linux) before placing the binary on your `PATH`. Each release includes static archives, SHA-256 checksums, SPDX SBOMs, Sigstore verification bundles, and SLSA provenance. GitHub-generated source archives are documentation only; they are not verified Aient CLI release artifacts. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient version aient auth login --print-url aient auth status ``` ## Customer Agent conversations From a configured project, select the refresh-backed customer OAuth profile explicitly with `aient --profile NAME agent ...`. Ambient project access is sandbox-scoped and intentionally does not grant Agent conversation authority. Find a reconnectable Agent Thread or attach to one: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient agent list [--search QUERY] [--status all|active|closed] [--include-archived] [--limit 1-100] [--cursor CURSOR] [--json] aient agent status THREAD [--json] aient agent events THREAD [--follow] [--json] aient agent message THREAD MESSAGE... [--operation-id UUID] [--immediate] [--capability computer-use] [--json] aient agent respond THREAD INTERACTION ANSWER... [--operation-id UUID] [--json] aient agent cancel THREAD [--operation-id UUID] [--json] aient agent chat THREAD aient agent execution status EXECUTION [--json] aient agent execution attach EXECUTION aient agent execution wait EXECUTION [--json] aient agent execution cancel EXECUTION [--json] ``` `agent list` accepts an exact Thread ID or title query plus explicit status, archive, page-size, and cursor filters. Its human continuation command preserves the active filters; `--json` emits one versioned page. List, status, and events require `aient.agent.read`; messages, answers, explicit cancellation, and chat mutations require `aient.agent.write`. `status --json` emits one deterministic document. `events --follow --json` emits JSONL and can resume after a durable sequence. Stable Thread, task, message, interaction, operation, sandbox, and execution identifiers preserve the reconnect path after terminal or network loss. `--immediate` requires one explicit stable operation UUID; an ordinary message may omit it and let the CLI generate one. Reuse an explicit UUID only for an exact retry. Exact text and delivery mode return `duplicate`; changed text or mode returns `operation_conflict`. Immediate delivery never cancels or replaces the active task or changes its tool scope. The receipt reports `steered`, `started`, `queued`, or `duplicate`; incompatible active-task origin or attachment plans fail with `cooperative_steering_scope_mismatch`. `--capability computer-use` selects compact browser and Linux desktop accessibility observations, native input and optional screenshots for the task created by this message. It queues behind active work, rejects `--immediate`, and leaves later ordinary messages using Agent defaults. The runtime manages setup, recording and cleanup; existing permissions and sandbox admission still apply. An exact retry must preserve the message, operation ID and capability selection. Inside `agent chat`, enter `! COMMAND` to run a supervised shell command in the environment and sandbox frozen from the latest rendered Agent status. The CLI shows the target and preallocated execution UUID before output, passes no workstation process environment or local repository inference, and sends no Agent message. Reconnect by passing the exact `EXECUTION` identifier to the execution commands above. Ctrl-C in ordinary chat detaches without cancelling Agent work; Ctrl-C in the active remote-command view explicitly requests cancellation of that exact execution. This release requires an existing `THREAD`; it does not start a new Agent Thread. The standard `aient-agent` catalog uses the V3 workspace writer. The retained `aient-pr-e2e` catalog remains the V2 rollback control. The server keeps the closed V2 and V3 readers and negotiates capabilities per template; the CLI does not infer one global workspace layout version. ### Sync selected files into an Agent checkout ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --profile "$PROFILE" agent worktree sync THREAD /absolute/local/repository \ --repository OWNER/REPO --path apps/frontend/src/App.tsx --json ``` Repeat `--path` for 1–128 existing regular files, each at most 64 KiB. This one-shot command targets the Agent’s current primary checkout on a compatible runtime; unsupported runtimes and another repository are rejected. Selection follows Git/extras policy; `--include` explicitly allows ignored source. Git control files, `.aient`, active credentials, symlinks and submodules are rejected. Each write atomically replaces an existing single-link regular destination, preserving its mode; remote parents must already exist. Whole-file updates use last completed write wins, while the model’s line-hash edit checks remain in force. Agent ownership, incarnation and lease remain unchanged; Git metadata is not synchronized. JSON separates confirmed `writes` from `notification`. Exact path, byte count, SHA-256 acknowledgement and terminal exit zero confirm each write. After the batch succeeds, a cooperative path-change notice asks the Agent to reread. Delivery does not prove a model read or application reload. Failed or ambiguous writes send no success notice; earlier confirmed writes remain applied. If notification alone fails, retry only `agent message` with the returned exact text, operation ID and `--immediate`. From a configured Aient Git project on macOS, login establishes bounded project access by default. The refresh credential stays in Keychain while launchd rotates only a short-lived repository-private envelope for up to 90 days. Login returns only after manager-owned renewal is verified. No foreground terminal or PID owns renewal. New installations persist the directory birth time so an APFS device-number change across a remount does not invalidate the same protected project. A legacy installation whose APFS device already changed still fails closed once; if the host profile is healthy, `aient --profile NAME auth export --store-in-project` replaces it without another login. Linux managed renewal remains preview pending a real systemd-user and D-Bus Secret Service renewal, restart, and logout canary. Use `--project-access=none` for host-only login, managed-project opt-out, or to avoid the Linux preview. ## Quick start From a local Git checkout: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development --size medium -- pnpm test ``` With `--environment`, the CLI synchronizes the current workspace by default. It shows the organisation, resolved environment, inferred or selected repository, and brokered GitHub capabilities before creating the sandbox. Use `--size small|medium|large` to request a product-owned compute class, or omit it to use the environment default. The server owns the underlying template and resource envelope. Every customer-development create advertises `developmentSizeBindingVersion: v2`. A compatible product response binds exact `aientSizeBinding=v2`, `aientSizeClass`, `aientSizeSource=requested|environment-default`, and `aientSizeEnvelope=v1|v2` selectors into the signed request target. Partial, duplicated, unknown, crossed, or mismatched bindings fail before the control-plane request. Legacy explicit V1 and an old server's unbound environment default remain supported during rollout. This size-binding reader does not choose the workspace writer; the standard `aient-agent` catalog uses V3 independently. ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Organisation: Example Engineering Environment: development Repository: example/widget (inferred from origin) GitHub access: branch push, pull requests ``` The local checkout is the workspace source. A configured Git remote is only a repository selector. The Aient service verifies membership, environment policy, repository ownership, and the active GitHub App installation before granting any provider operation. > [!NOTE] > > An organisation administrator must enable customer CLI workspaces for the selected environment. If it is disabled, the command fails before sandbox creation with `HTTP 403: development environment is not enabled for CLI workloads`. ## Choose a workflow Use `sandbox run` for a disposable command: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development --size medium -- go test ./... ``` Use `--keep` when you want to retain the created sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development --keep -- pnpm install aient sandbox list --environment development ``` Use detached ownership when the command must survive the initiating client. A composed run must also be retained: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development --name background-tests --detach --keep -- pnpm test aient sandbox execution status background-tests EXECUTION_ID --environment development aient sandbox execution attach background-tests EXECUTION_ID --environment development aient sandbox execution wait background-tests EXECUTION_ID --environment development ``` Every ordinary CLI process starts one fresh execution. Recovery inside that process reuses the exact execution ID and never redispatches the command; running the same command in another process starts another execution. Before the admission request, `sandbox run --json` and `sandbox exec --json` emit `execution_started` with `executionId` and `outputCursor` after publishing the cursor-0 local pointer. This identifies the admission attempt for reconnect; it does not prove that the server admitted or started the command. The validated server admission frame remains authoritative. `sandbox execution attach SANDBOX EXECUTION --after-cursor CURSOR` replays supervisor-retained output after the caller's last accepted cursor, then follows live output. The cursor advances only after a complete frame is presented, so reconnect may repeat one frame but never silently skips output. While a client is connected, the command runs at the client's pace. If a reconnect resumes after output the server no longer retains, stdout keeps only the bytes received before the gap, the retained tail goes to stderr between an `aient: output incomplete: bytes FROM-TO lost; ...` header that names the command's own exit status and an `aient: output incomplete: end of retained tail` line, and the exit status is 74. `--json` reports the gap as an `execution_output_gap` event and also exits 74. `--timing-json` emits one content-free JSON line on stderr without changing command output or exit status. It records process-relative preflight, create-ready, proven workspace activation, first output, truthful terminal status, downloads, and confirmed cleanup or retention. Same-execution recovery never dispatches a replacement command, and a timing-writer failure cannot mask the command result. Automatic disposable cleanup performs exactly one DELETE with the full configured `--timeout`, then a fresh equally bounded GET-only absence check. Ambiguous deletion is not retried. A failed lease restore keeps the primary error authoritative and reports `retained=true` with `cleanupConfirmed=false`; genuine lease-refresh failures still warn and retry while an active heartbeat continues. A foreground CLI reconnects the same execution during a bounded grace. If it must leave a potentially live execution, it retains the sandbox and prints the exact attach command. Cross-process reattachment is explicit; an ordinary new run always starts a fresh execution. Use a named retained sandbox when you want follow-up lifecycle reads and supervised execution: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development --repository owner/repository \ --size medium \ --name laptop-offload --keep -- true aient sandbox status laptop-offload \ --environment development --repository owner/repository aient --timeout 15m sandbox exec laptop-offload \ --environment development --repository owner/repository \ --workdir /workspace/repo -- go test ./... # After your next local edit, synchronize and execute in the same sandbox: aient sandbox exec laptop-offload --workspace . \ --environment development --repository owner/repository \ -- go test ./... aient sandbox logs laptop-offload \ --environment development --repository owner/repository aient sandbox delete laptop-offload \ --environment development --repository owner/repository ``` Environment/repository-bound retained calls must repeat both selectors; repository-independent calls repeat only `--environment`. Without the matching selectors, the owner-only client deliberately cannot see the bound sandbox. `exec --workspace PATH` accepts ordinary sync source flags and runs in the synchronized directory unless `--workdir` is explicit. A failed sync never starts the command; rerun it to sync the worktree as it is now. The sandbox keeps its existing owner and retention policy; sync may renew its lease. This command neither creates nor releases it. `--detach` waits for sync and execution acceptance. For this combined command, `--timing-json` reports synchronization only, not command execution or output latency. `sandbox run` and `sandbox exec --workspace` also accept `--workspace-commit` with a full Git commit ID. The selected committed tree and its selected submodule Gitlinks sync without staged, unstaged, or untracked source edits. Exact-commit mode requires the default remote directory and cannot combine with extra-file selections, resets, or automatic node\_modules preservation. If private HTTPS submodule acquisition needs a global credential helper or URL rewrite, initialize the submodule with your normal Git setup or configure an SSH submodule URL override. To select ignored and untracked non-Git paths broadly, omit regular AppleDouble sidecars, and subtract explicit exclusions: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --exclude '**/node_modules/**' \ --exclude '.local/**' \ --exclude '**/.env*' \ -- go test ./... ``` Exclude-only selection never replaces Git-tracked content or includes `.git` metadata. Regular macOS AppleDouble `._*` files are omitted from broad non-Git selection before archive, manifest, byte-count, or digest construction; the local source remains unchanged. There are no implicit cache or secret exclusions, so review the selected scope and explicitly exclude `.env` files, credentials, tokens, private keys, and other secret-bearing non-Git paths. To replace an exact generated remote directory without changing its local contents: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --reset-remote-dir packages/otel/dist \ -- pnpm install ``` `--reset-remote-dir` is repeatable explicit authority. It publishes an ordinary empty candidate-owned directory through atomic activation so build cleanup can remove or recreate it. On legacy servers, other ignored remote state keeps its existing preservation behavior. When explicit repository path ownership is advertised, unselected non-Git paths are absent unless named by `--preserve-remote-dir`. Reset paths must be exact repository-relative non-Git directories and cannot be combined with `--exclude`. In that negotiated mode, unselected generated, dependency, and cache directories are absent from a new candidate instead of becoming inferred preservation mount roots. Package tooling can remove and recreate them normally. To retain an exact ordinary remote directory across synchronization: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync retained-sandbox . \ --environment development --repository owner/repository \ --preserve-remote-dir .cache/tool ``` `--preserve-remote-dir` is repeatable explicit authority. Preservation roots must be exact repository-relative non-Git directories and cannot overlap Git content, selected uploads, reset paths, symlinks, files, or one another. A server without explicit path-ownership support rejects preservation instead of silently changing its meaning. `--preserve-node-modules` adds every pnpm `node_modules` directory that already exists in the sandbox: the root and each package listed in `pnpm-workspace.yaml`, outside submodules. Installed dependencies then survive a repeated sync, and an unchanged workspace stays a no-op. For a deliberately incomplete parent-only checkout when submodules are initialized locally: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --submodules=gitlinks \ -- go test ./... ``` `--submodules=gitlinks` recursively verifies every initialized child is clean, untracked-free, and checked out at the exact parent-index object before any remote request. It omits child bytes and binds the complete sorted parent-index Gitlink set into manifest-backed activation. This is an incomplete Gitlink-only workspace, not recursive submodule transfer; commands that need child files must initialize them separately. To transfer only exact nested repositories consumed by the remote command: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --submodules=selected --submodule apps/aient-docs \ -- pnpm --filter aient-app build ``` Selected children must be initialized, clean, complete-history repositories at their exact parent-index Gitlink OIDs. Repeat `--submodule` for every selected ancestor; selection never expands recursively. Initialized but unselected children contribute no bytes and remain empty Gitlink boundaries remotely. Selected submodules may be combined with declared parent-repository extras; the server verifies every declared path and atomic exchange before activation. Capability preflight recognizes the exact old helper generation mounted by an already-running sandbox. A reader without exact selected-submodule support rejects the operation rather than weakening the policy. Default snapshots keep raw uninitialized Gitlink index paths exact without applying explicit-manifest UTF-8, backslash, or entry-count limits. Those snapshots emit no Gitlink manifest. Explicit `--submodules=gitlinks` remains fail-closed and applies the portability rules plus the 512-entry bound before any remote request. ## Experimental selected-file live sync Select existing local regular source files in an already running sandbox, publish them once, then watch ordinary local saves. Pass the actual remote worktree explicitly: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync SANDBOX /absolute/local/repository \ --environment development --repository owner/repository \ --live-stream --remote-dir /workspace/repo/actual-worktree \ --live-path apps/aient-app/src/app/example/page.tsx ``` Repeat `--live-path` for 1–128 exact repository-relative files, each at most 32 MiB. The initial upload overwrites those selected files: choose files whose overwrite is intended. The ordinary tracked Git and non-Git include/exclude policy applies; Git control paths, credentials, symlink sources and submodules are rejected. Parent directory watches support ordinary editor atomic saves; identical captured content is skipped. The sandbox keeps running when the watcher ends. `--live-stream` requires a compatible attested native V3 workspace, existing remote parent directories and normal customer shell permission. It sends captured bytes over one authenticated terminal WebSocket. Each file replaces atomically; the exact sequence, byte count and SHA-256 acknowledgement proves individual file delivery. Setup and helper READY are measured separately from per-file delivery. The session holds a shared workspace fence, blocking ordinary full sync and exclusive files API writes. Ctrl-C requests helper STOP and waits for terminal command completion. Missing proof prints `termination UNVERIFIED`: the helper and fence may remain active and keep blocking those writes. The reported session/PID is diagnostic metadata, not authority to kill a process. Restarting a watcher does not recover an unverified session and overwrites the selected files again. Use the separate experimental `--live` mode instead of `--live-stream` to upload selected files through the files API. It requires an attested existing workspace and normal files permission, and reads the workspace binding for each save batch. Its diagnostics distinguish binding reads and upload duration. The files transport remains the default. A long-running app on a compatible runtime holds a shared workspace fence, which can block those exclusive binding reads and uploads. For a compatible customer workspace bound to its original allocation, opt into finite supervised execution for each selected file: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync SANDBOX /absolute/local/repository \ --live --live-transport exec --remote-dir /workspace/repo/actual-worktree \ --live-path src/example.ts ``` Start this watcher before starting a long-running app: the one-time capability bootstrap still requires an exclusive fence. After bootstrap, each initial write and changed save uses normal customer exec authorization and a shared fence, compatible with ordinary app execution. There is no per-save capability read or files API upload. Each selected file must already exist remotely as a single-link regular file with existing ordinary parent directories, and each captured source is limited to 64 KiB. Replacement preserves the remote mode. Unsupported targets and operator profiles reject this transport; there is no automatic fallback. Only an exact path, byte-count and SHA-256 ACK together with terminal exit 0 confirms delivery. Missing proof reports `write UNCONFIRMED`, stops the watcher without retry, and means remote bytes may have changed; reconcile selected files before restarting. Each command is bounded to 30 seconds, and sandbox hard expiry still applies. Diagnostics measure save detection through confirmed ACK; application visibility must be measured separately. Both live modes require explicit `--remote-dir` and `--live-path`; choose only one mode. `--live-transport` requires `--live`. Both modes perform no snapshot activation, application restart or Git metadata synchronization. Deletions, renames, new unselected files, mode changes, concurrent edit protection, submodules, overflow/reconnect recovery and multi-file consistency are unsupported. Errors stop the watcher. Delivery does not prove application readiness or browser HMR, and there is no universal subsecond latency guarantee. Existing finite sandbox leases and hard expiry apply. ## Workspace operations Workspace operations live in `~/.aient/workspace-operations` only while the command that started them runs. When a `sync`, `run` or `exec --workspace` operation ends, whether it activated, failed or was cancelled, nothing of it is kept there. If a sync is interrupted, rerun the command: it syncs the worktree as it is now; nothing is resumed. `sandbox run` deletes its sandbox after a failed sync unless `--keep` is given. Removing the local record can itself time out or fail, for example while another process holds the store lock for more than 10 seconds. That never changes the result: the CLI prints one line saying the record could not be removed yet, a synced workspace stays synced, `sandbox run` still runs its command, and the next sync or `sandbox workspace list` removes the record. Otherwise only a killed CLI leaves a directory behind, and the next sync or `sandbox workspace list` removes it if this version created it. Records that older aient CLIs created are kept, because those releases took no lock that shows whether they are still in use; delete one by hand once no older CLI is running. `list` shows the store without authentication or network access; `status` and `discard` reconcile the server side of an operation that is still recorded: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox workspace list [--json] aient sandbox workspace status OPERATION aient sandbox workspace discard OPERATION ``` `aient sandbox create --env KEY=VALUE` (repeatable) sets an environment variable for the sandbox's processes. The values are stored with the sandbox and are not secret; use environment secrets for credentials. A sandbox whose start-up keeps failing ends `failed`, and the create prints its reason. When one selected submodule cannot use the Git cache (a network, DNS or sign-in discovery failure, an unavailable server, or a repository with no cache authority), that submodule uploads directly on its own while the root keeps its cached delta, instead of the whole workspace uploading. A cancellation, a changed capture, or an authoritative refusal of that repository (HTTP 401, 403, 409, or 404 other than an unregistered repository) still stops the cache operation. The root cache lookup and root seed start retry network failures up to three attempts. ## Pull request reviews `aient review OWNER/REPO#NUMBER` asks aient-cto\[bot] for an independent review of a pull request's current head, posted as a GitHub review: an approval, a change request with inline comments, or a comment without a verdict. While it waits it prints progress (`queued`, then `reviewing`), then the outcome and the review's link (`--no-wait` returns once the review is requested, `--json` prints the review). A repeat for the same head returns the review already given, at once. It needs the repository connected to your organisation and the Aient CTO GitHub App installed on it, and uses the `aient.agent.write` scope (plus `aient.agent.read` to wait). When no review can be given, it says what to do and exits non-zero. ## Environment secrets Organisation owners and administrators can replace an encrypted environment secret after consenting to the dedicated write capability: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} printf '%s' "$STRIPE_SECRET_KEY" | aient environment secrets set \ --environment development STRIPE_SECRET_KEY --stdin ``` The command never returns the secret value. Server-managed environment secrets are separate from local files selected for workspace synchronization. ## Runtime service mappings Inspect discovered runtime services and repository paths, or classify one service explicitly: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient services list aient services paths owner/repository aient services classify web --repository owner/repository --path apps/web aient services apply --file service-mappings.json --json aient services apply --file service-mappings.json --apply --json ``` `services apply` is dry-run by default. These commands require the `services:read` and `services:manage` OAuth scopes; existing sandbox access remains valid, while an older profile may need one interactive login before service management. ## Current release boundary The commands documented in this section are the surface shipped in Aient CLI 0.11.41. * Environment-bound `status`, `wait`, and `logs` resolve the repeated environment selector through the authenticated profile. * Exclude-only workspace selection starts from the broad eligible non-Git inventory, keeps Git state authoritative, omits regular macOS AppleDouble `._*` files by default, and applies only caller-supplied exclusions beyond that documented filter. * Exact `--reset-remote-dir` selection replaces a named remote non-Git directory with an ordinary empty candidate-owned directory without modifying the local source or weakening atomic activation. * Unselected non-Git paths are absent under explicit repository path ownership, so generated and cache directories remain ordinary removable directories instead of inferred bind-mount roots. * Repeatable `--preserve-remote-dir` retains only exact declared ordinary remote directories; collision and size checks fail locally before upload, and unsupported servers reject explicit preservation. * Explicit `--submodules=gitlinks` omits clean exact child checkouts only after recursive local verification and binds the complete parent-index Gitlink boundary into V2 or V3 activation; it does not transfer submodule content. * Default snapshots preserve raw uninitialized Gitlink index entries without applying constraints that belong only to the explicit Gitlink manifest; explicit mode continues to require UTF-8 portable paths, reject literal backslashes, and enforce the 512-entry bound. * `sandbox workspace list [--json]` shows the operations other CLI processes are running and removes directories a killed CLI of this version left behind. See [workspace operations](#workspace-operations). * Idle and timeout cleanup acts only on the exact sandbox revision it evaluated. This protects an accepted concurrent refresh without renewing or extending the operation lease or server-owned hard expiry. * Customer create and run support the `small`, `medium`, and `large` product-owned compute classes. * Customer-development create advertises `developmentSizeBindingVersion: v2` and accepts only a complete exact `aientSizeBinding=v2`, `aientSizeClass`, `aientSizeSource=requested|environment-default`, and `aientSizeEnvelope=v1|v2` binding. Invalid combinations fail before the control-plane request; legacy explicit V1 and an old server's unbound default remain rollout-only compatibility paths. * A conditional one-use `repository-prewarm` path handles an exact-clean same-HEAD workspace at the default repository root. A matching Ready member sends zero committed Git bytes and skips sandbox creation, MinIO reads, and workspace materialization; typed no-ready continues the same immutable capture cold, while an ambiguous claim stops without duplicate work. Dirty overlays, selected repositories, custom roots, explicit names, and other ineligible requests stay on the existing path. Ready-member supply is operator-bounded and not promised for every command. * Immutable capture overlaps remote preflight. Compact JSON cache phases remain one object per physical line, and `--timing-json` reports `gitCache.outcome=repository_prewarm` with zero client bytes on a hit. * A stale derived commit-graph that references only pruned unreachable objects does not block capture. Refs and reachable objects remain authoritative, the CLI does not rewrite user Git metadata, and a genuinely missing reachable object still fails before upload. * Named sandbox creation is replay-safe under bounded ambiguous-transport retries: one public name keeps one idempotency identity, matching fingerprinted settings adopt the accepted sandbox, changed fingerprinted settings conflict, and `sandbox run` never executes in or deletes a sandbox whose returned identity is unproven. * Organisation selection is bound to one canonical lowercase UUID. The CLI resolves the authenticated display name and canonical organisation UUID for human-readable status and trusted legacy-project migration guidance; the display name is render-only, and every present authenticated identity source must agree before the requested operation. * Environment-bound sandbox commands carry one authenticated organisation/environment/repository tuple across create, synchronization, files, execution, observation, lease refresh, and deletion. New customer workspace operations record that tuple in `workspace-operation-bundle-v2` and use the exact authority for status and discard instead of weakening to owner-only access; legacy V1 receipts keep their established compatibility behavior. * The conservative supervised execution timeout ceiling is `23h56m18s`. Larger `sandbox exec` command budgets fail locally before a sandbox request and at server admission before claim creation or provider dispatch with `sandbox_execution_timeout_invalid`; a valid budget can still conflict with the remaining hard lease of an older retained sandbox. * `aient auth exec --profile NAME -- COMMAND...` gives a trusted same-UID host wrapper one renewable access-only credential while injecting or copying no refresh, profile, operator, or imported portable authority. It is credential minimization, not a local process sandbox; untrusted same-UID descendants require external confinement. * From a configured project, `aient auth login` installs bounded manager-owned project access by default. Refresh authority remains in the OS credential store; ordinary repository commands discover only the rotating short-lived envelope with no profile/token flags, foreground exporter, or PID tracking. * Explicit `--submodules=selected` transfers only the named clean exact Gitlink dependency closure; unselected initialized children contribute no bytes and remain empty boundaries. * Selected submodules may be combined with declared parent-repository extras. The server verifies their exact receipts and capability preflight recognizes the mounted old helper generation. * `sandbox run` uses supervised execution and allocates one fresh execution per ordinary CLI process. `--json` emits `execution_started` with `executionId` and `outputCursor` before command output; same-process recovery and explicit `--after-cursor` attach replay retained output after the last accepted cursor and never redispatch the command. * `sandbox run --timing-json` writes one content-free stderr receipt for proven workspace activation through cleanup or retention without changing command output or exit status. * Automatic cleanup issues exactly one DELETE with the full configured `--timeout`; ambiguous deletion remains an error, and genuine lease-refresh failures still warn and retry. * `aient services list|paths|classify|apply` manages explicit runtime-service repository mappings. Batch apply is dry-run by default and uses the dedicated `services:read` and `services:manage` OAuth scopes. * Owner/admin environment-secret writes are encrypted, capability-gated, and write-only from the CLI. * Synchronization to `/workspace/repo` negotiates V3 immutable-candidate activation when an exact attested server advertises the closed V3 capability; it retains typed V2 on V2 servers and compatible V1 on older servers. V3 selection rejects unknown, duplicate, mixed, or invalid capability evidence before upload and never falls back after selection. * Workspace synchronization publishes complete, self-contained parent-repository Git history. Shallow, promisor, alternate-backed, grafted, or replace-influenced local sources fail before upload; incompatible sandbox helpers fail before probe or mutation instead of selecting a shallow fallback. * V2 workspace activation returns success only after the exact activated revision is certified under the same exclusive workspace-operation fence. If the result is lost after commit, the CLI reconciles the same immutable operation within the command and never replays the requested command; a rerun syncs fresh. Separately, genuine cross-writer fence contention can still return typed `workspace_layout_certification_busy`; workspace-backed `sandbox run` retries only that exact pre-dispatch response for up to 10 seconds, honors `Retry-After`, and never replays a user command. * `aient agent list|status|events|message|respond|cancel|chat` finds or attaches to an existing Agent Thread through the public API. It does not start a new Agent Thread. * `agent events --follow --json` emits JSONL after durable sequence replay; Ctrl-C detaches ordinary chat without remote cancellation. * `! COMMAND` in Agent chat runs only in the frozen reported sandbox through supervised execution. Reconnect with `agent execution status|attach|wait|cancel`; no local shell, caller environment, repository inference, or Agent message is added. * Terminal `agent execution status|wait|cancel` output failure preserves the local selector for a fresh authorized status read of the same execution; successful terminal delivery still retires it. `wait` preserves a nonzero exit even if local retirement fails. Recovery requires surviving server evidence and current authorization, not permanent execution history. * Supervisor-retained output is cursor-replayable. Cursor acceptance is frame-atomic, so reconnect may repeat the last complete frame but cannot create a gap; retention remains bounded rather than permanent command history. * `sandbox shell` always opens a new shell. Shell reattachment and shell history are unavailable. * Selected-file `sandbox sync --live|--live-stream` is experimental. Native V3 `--live-stream` requires normal customer shell permission and holds a shared workspace fence. After `termination UNVERIFIED`, the helper/fence may remain active and block full sync or exclusive files API writes; starting another watcher does not recover it. Neither mode provides conflict protection, deletion/rename propagation, multi-file consistency or reconnect recovery. File ACKs do not establish application readiness, browser HMR or universal subsecond latency. * `aient sandbox port SANDBOX PORT` publishes a public HTTPS capability URL on a configured native deployment; `aient sandbox port SANDBOX --revoke` removes the binding. Anyone with the URL can access the application. The binding follows the remaining lease and exact incarnation, without another retention owner. Server-side preview routing, certificates, DNS, and exposure policy must be configured; installing the CLI does not establish availability. Agent-owned previews, raw TCP forwarding, and local-resource forwarding are not promised. * Customer create and run accept `--cpu` and `--memory` requests within the selected size's server-owned limits. Custom requests select the native agent-sandbox provider and bypass preset warm claims; GPU requests remain unsupported. Resource limits and templates remain operator controls. See [Current limits and troubleshooting](/ai-tools/cli/troubleshooting) for recovery guidance and common errors. # Authentication and project profiles Source: https://docs.aient.ai/ai-tools/cli/authentication Use browser OAuth, durable project access, named profiles, access-only exports, and restricted task credentials. # Authentication and project profiles Aient CLI uses customer OAuth for ordinary development work. Refresh credentials stay in the operating-system credential store; local profile and project files contain non-secret selectors only. ## `aient auth login` Open the browser consent flow and save a refresh-backed customer session: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth login --print-url aient auth login --profile client-a --print-url ``` | Flag | Meaning | | - | - | | `--profile NAME` | Save or replace the selected named customer profile. | | `--issuer URL` | OAuth issuer. The default is `https://aient.ai`. | | `--callback-timeout DURATION` | Maximum time to wait for browser consent. The default is `5m`. | | `--print-url` | Print the one-time authorization URL instead of opening a browser. Keep the command running while approving it. | | `--project-access=auto` | In the nearest configured project, install or reconcile bounded managed access. This is the default. | | `--project-access=none` | Keep only the host login and remove managed access for the nearest configured project. | On macOS, project access keeps refresh authority in Keychain and installs a launchd user service that rotates a 15-minute envelope under the Git common directory for up to 90 days. Login returns only after that exact manager-owned renewal is verified. The terminal can then close; no foreground process or PID owns renewal. Linux managed renewal remains preview pending a real systemd-user and D-Bus Secret Service renewal, restart, and logout canary. Use `--project-access=none` for host-only login when you do not explicitly want to opt into that preview. Use a distinct profile for each organisation context you work in. Existing OAuth sessions may need a new login when Aient introduces additional consented capabilities. Customer MCP management adds `mcp_connections:read` and `mcp_connections:manage`. Run `aient auth login` again if an existing profile receives `403` for `aient environment mcp` after upgrading to a CLI release that includes those commands. ## `aient auth status` Verify the selected login and display its issuer, organisation, and granted scopes: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth status aient auth status --profile client-a aient auth status --all ``` `auth status --all` displays local metadata for every profile without refreshing credentials. A status check for a selected refresh-backed profile verifies the live session. From a configured project with no explicit profile, status discovers the repository-private access envelope, verifies its product context, and reports safe lease metadata without reading host refresh authority. A present invalid or expired project envelope fails closed instead of falling back to a host profile. An explicit `--profile` selects the host recovery view, including derived managed-service health. ## `aient auth logout` Revoke and remove only the selected login: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth logout --profile client-a ``` Before revoking the selected profile, logout stops and removes every managed project service installed for that profile. A cleanup failure is reported while credential revocation still proceeds. Logging out one profile does not remove another organisation's profile. ## `aient auth profiles` List locally known named profiles: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth profiles ``` The output includes the current fallback marker and non-secret organisation and issuer metadata. It never prints access or refresh tokens. ## `aient auth profiles default` Choose the non-secret fallback profile: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth profiles default client-a ``` The default is used only when no explicit access token, explicit profile, environment profile, or project profile selects another context. ## Bind a checkout with `.aient/config.yaml` Create a project file in the checkout or linked worktree: ```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}} profile: client-a organisation: 11111111-1111-4111-8111-111111111111 repository: example/widget ``` The CLI searches from the selected workspace or current directory upward and uses the nearest `.aient/config.yaml`. Supported keys are: | Key | Purpose | | - | - | | `profile` | Select a locally logged-in named profile. | | `organisation` | Assert the expected canonical lowercase organisation UUID during preflight. | | `repository` | Supply an `owner/name` repository selector when Git remotes are absent or ambiguous. | The file is intentionally strict: it accepts only these scalar keys and must not contain credentials. The organisation and repository values are selectors and mismatch checks, never authority. ### Profile resolution order For each invocation, Aient resolves authentication in this order: 1. An explicit access-only source: `--access-token-file`, `AIENT_ACCESS_TOKEN_FILE`, or `AIENT_ACCESS_TOKEN`. 2. `--profile NAME`. 3. `AIENT_PROFILE`. 4. A valid `/aient/access-v1.json` belonging to the nearest configured project. 5. The profile named by the nearest `.aient/config.yaml`. 6. The user default selected by `auth profiles default`. 7. Compatibility profile `customer`. Conflicting explicit sources fail locally. For example, different values in `--profile` and `AIENT_PROFILE` are rejected instead of silently choosing one. A portable access token and refresh-profile selection are also mutually exclusive for the same invocation. ## `aient auth export` Export a shorter-lived access-only credential for a child process, CI job, or another machine. Prefer a uniquely named, mode-`0600` file outside every uploaded workspace: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} TOKEN_DIR="$(mktemp -d)" cleanup_aient_access() { rm -f "${TOKEN_DIR}/access.json" rmdir "${TOKEN_DIR}" 2>/dev/null || true } trap cleanup_aient_access EXIT aient auth export --profile client-a --format file --ttl 10m \ --output "${TOKEN_DIR}/access.json" aient --access-token-file "${TOKEN_DIR}/access.json" \ sandbox list --environment development cleanup_aient_access trap - EXIT ``` | Flag | Meaning | | - | - | | `--profile NAME` | Required refresh-backed source profile. | | `--format env` | Print a shell assignment containing the live bearer token. | | `--format json` | Print a JSON access envelope containing the live bearer token. | | `--format file` | Atomically write a mode-`0600` JSON access envelope. | | `--output PATH` | Required destination for `file` format. | | `--ttl DURATION` | Requested lifetime from `1m` to `1h`; default `15m`. Actual expiry may be shorter. | | `--renew-for DURATION` | Keep an explicit file export renewed for `1m` to `90d`; the foreground caller owns that lifecycle. | | `--store-in-project` | Install or reconcile manager-owned access for the nearest configured project for up to 90 days. | Use the project form to repair an already logged-in host profile without re-running browser OAuth: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --profile client-a auth export --store-in-project ``` In Aient CLI 0.10.16 and newer, this returns after the manager-owned renewer is verified. It is not the old foreground exporter contract. > [!WARNING] > > The `env` and `json` formats deliberately print a live bearer token. Use them only when the receiving system requires stdout, disable shell tracing, keep the output out of logs and transcripts, and unset or delete the credential when the receiving process finishes. An export contains no refresh token, environment secret, repository credential, or GitHub token. Imported access-only credentials are not refreshed or persisted by the CLI. When one expires, export another or sign in on that machine. The CLI refuses to upload the active access-token file—or a hard link to it—through workspace sync, raw upload, or `files put`. ## `aient auth exec` Run a trusted local task with a renewable, access-only credential rather than exposing the selected profile or its refresh token: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth exec --profile client-a -- ./trusted-wrapper ``` The child receives `AIENT_ACCESS_TOKEN_FILE` only. The CLI removes Aient profile, token, and sandbox-control variables from the child environment, creates a temporary access-only file, renews it while the task runs, and removes it when the task finishes. `auth exec` requires a refresh-backed customer profile. It rejects an imported access token and operator authority. If you do not provide `--profile`, the nearest project configuration must contain a canonical organisation UUID. By default, the delegated credential covers sandbox reads, creation, deletion, execution, shell access, file operations, and authorised environment use. Use repeatable `--scope` only when the task needs a different exact scope set; it replaces the default set. Repository-write and pull-request scopes also require an exact project repository, a matching `origin` or `upstream` remote, and `--confirm-repository OWNER/NAME`. > [!WARNING] > > `auth exec` minimises Aient credentials; it is not a local process sandbox. A same-user child can still reach the host filesystem, home directory, credential store, session, and sibling processes. Use an external confinement boundary for untrusted code. ## Environment variables | Variable | Use | | - | - | | `AIENT_PROFILE` | Select one named refresh-backed profile. | | `AIENT_ACCESS_TOKEN` | Supply one raw short-lived access token. Treat as a secret. | | `AIENT_ACCESS_TOKEN_FILE` | Read one exported mode-`0600` access envelope. | # CLI command reference Source: https://docs.aient.ai/ai-tools/cli/command-reference A compact index of current Aient CLI commands and global flags. # CLI command reference This page indexes the current customer CLI surface, including Aient CLI 0.11.31. Use `aient COMMAND --help` to confirm the flags supported by your installed version. ## Authentication | Command | Purpose | | - | - | | `aient auth login [--print-url] [--project-access=auto\|none]` | Sign in through browser OAuth and, by default, reconcile bounded project access. | | `aient auth status` | Verify and display the selected login or repository-private project lease. | | `aient auth logout` | Stop the selected profile's managed project services, then revoke and remove the login. | | `aient auth profiles` | List named non-secret profile metadata. | | `aient auth profiles default NAME` | Set the fallback customer profile. | | `aient auth export` | Deliberately export a short-lived access-only credential. | | `aient auth export --store-in-project` | Repair or reconcile manager-owned access for the nearest configured project. | | `aient auth exec -- COMMAND...` | Run a trusted task with a renewable, restricted Aient credential. | See [Authentication and project profiles](/ai-tools/cli/authentication). ## Sandboxes | Command | Purpose | | - | - | | `aient sandbox list` | List visible sandboxes. | | `aient sandbox create` | Create a retained sandbox. | | `aient sandbox status SANDBOX` | Show sandbox state. | | `aient sandbox wait SANDBOX` | Wait for readiness. | | `aient sandbox sync SANDBOX [LOCAL_WORKSPACE]` | Synchronize exact Git and selected extra-file state. | | `aient sandbox sync SANDBOX LOCAL_WORKSPACE --live-stream --remote-dir PATH --live-path FILE` | Watch selected existing files over one shell stream (experimental). Use `--live` for the files API or `--live --live-transport exec` on a compatible runtime. See [live sync](/ai-tools/cli/run-and-sync#experimental-selected-file-live-sync). | | `aient sandbox run -- COMMAND...` | Create, run, and clean up a disposable sandbox. | | `aient sandbox exec SANDBOX -- COMMAND...` | Run a supervised command in a retained sandbox. | | `aient sandbox shell SANDBOX` | Open a new interactive shell. | | `aient sandbox docker bootstrap SANDBOX` | Start a tmpfs-backed Docker daemon through a detached supervised execution. | | `aient sandbox logs SANDBOX` | Read a bounded sandbox runtime log snapshot. | | `aient sandbox delete SANDBOX` | Delete a sandbox. | Use `--preserve-node-modules` on `sandbox sync` or `sandbox exec --workspace` to [keep existing pnpm dependencies](/ai-tools/cli/run-and-sync#keep-installed-pnpm-dependencies). See [Run and synchronise workspaces](/ai-tools/cli/run-and-sync) and [Retained sandbox lifecycle](/ai-tools/cli/retained-sandboxes). > [!WARNING] > > An environment-bound customer sandbox requires `--environment` on `status`, `wait`, and `logs`, and its original environment and repository selectors on `sync`, `exec`, and `execution`. `files` and `shell` apply only to sandboxes without an environment binding. ## Workspace operations | Command | Purpose | | - | - | | `aient sandbox workspace list [--json]` | List operations other CLI processes are running, and remove records a killed CLI left behind. | | `aient sandbox workspace status OPERATION` | Inspect the remote phase of a workspace operation that is still recorded. | | `aient sandbox workspace discard OPERATION` | Release an uncommitted publication after the server confirms it is safe. | A workspace-backed `sandbox run`, `sandbox exec --workspace` or `sandbox sync` keeps its operation record only while it runs. To recover from a failed or interrupted sync, rerun the command; it syncs the worktree as it is now. See [Run and synchronise workspaces](/ai-tools/cli/run-and-sync#recover-from-an-interrupted-workspace-sync). ## Agent worktree reads | Command | Purpose | | - | - | | `aient agent worktree read THREAD PATH --repository OWNER/REPO --output NEW.json` | Save one bounded, read-only baseline from an Agent's attached repository worktree. | See [Agent worktree reads](/ai-tools/cli/executions-and-files#agent-worktree-reads). The command requires a compatible server and the `aient.agent.read` and `aient.sandbox.files` grants. ## Agent worktree sync | Command | Purpose | | - | - | | `aient agent worktree sync THREAD LOCAL_WORKSPACE --repository OWNER/REPO --path FILE --json` | Send selected existing files to an Agent's current primary checkout on a compatible runtime and ask it to reread them. | Available from Aient CLI 0.11.27. Use a customer OAuth profile and repeat `--path` for each file. See [Agent checkout sync](/ai-tools/cli#sync-selected-files-into-an-agent-checkout) for file limits, overwrite behaviour, and notification recovery. ## Environment secrets | Command | Purpose | | - | - | | `aient environment secrets set --environment ENV NAME --stdin` | Set or replace one encrypted environment secret. | See [Environment secrets](/ai-tools/cli/environment-secrets). Only current organisation owners and administrators can set secrets. ## Supervised executions | Command | Purpose | | - | - | | `aient sandbox execution attach SANDBOX EXECUTION [--after-cursor CURSOR]` | Replay retained output after a cursor, then follow live output. | | `aient sandbox execution status SANDBOX EXECUTION` | Read content-free current state. | | `aient sandbox execution wait SANDBOX EXECUTION` | Wait for a content-free terminal outcome. | | `aient sandbox execution cancel SANDBOX EXECUTION` | Cancel the exact execution. | See [Executions, output, and files](/ai-tools/cli/executions-and-files). Repeat the original `--environment` and `--repository` selectors on every execution observation or cancellation of an environment-bound customer command. ## Explicit files | Command | Purpose | | - | - | | `aient sandbox files put SANDBOX LOCAL_PATH ABSOLUTE_REMOTE_DIRECTORY` | Upload one file or directory. | | `aient sandbox files get SANDBOX ABSOLUTE_REMOTE_FILE LOCAL_PATH` | Download one file. | | `aient sandbox files ls SANDBOX [ABSOLUTE_REMOTE_DIRECTORY]` | List a directory. | | `aient sandbox files rm SANDBOX ABSOLUTE_REMOTE_PATH` | Remove a path. | ## Utility commands | Command | Purpose | | | | | - | - | - | - | - | | `aient version` | Print version, source commit, and build time. | | | | | \`aient completion bash | fish | powershell | zsh\` | Generate shell completion. | | `aient help [COMMAND]` | Show help. | | | | ## Global customer flags | Flag | Meaning | | - | - | | `--profile NAME` | Select a named customer profile. | | `--access-token-file PATH` | Read one mode-`0600` access-only JSON envelope. | | `--timeout DURATION` | API request budget; for `sandbox exec`, also the remote command budget. For workspace-sync archives, this is an inactivity budget that resets while bytes flow or a response begins. Default `5m`. | | `--verbose` | Print safe request metadata and detailed progress. | | `--version`, `-v` | Print version information. | | `--help`, `-h` | Show help. | # Executions, output, and files Source: https://docs.aient.ai/ai-tools/cli/executions-and-files Observe supervised commands, reconnect to live output, and transfer explicit files. # Executions, output, and files `sandbox exec` creates one supervised execution and prints its ID before dispatch. The `sandbox execution` commands address that exact `(sandbox, execution)` pair without starting another command. ## Reconnect and replay output Use `attach --after-cursor CURSOR` to replay supervisor-retained output after the last accepted cursor, then follow live output. Retention is bounded rather than permanent command history. `status`, `wait`, and `cancel` return content-free execution state. An authorised child receives its process capabilities and can deliberately print a mounted environment or GitHub secret. During uninterrupted streaming, the CLI returns stdout and stderr verbatim to the connected caller, whose terminal or agent runner may record it. Avoid `env`, `printenv`, shell tracing, and echoing `GH_TOKEN`; avoid printing secrets into retained output. If an attached connection is interrupted, use the printed execution ID to query status first. Do not rerun an arbitrary command to discover whether it started. ## Incomplete output after a reconnect In Aient CLI 0.11.29, `sandbox exec` and `sandbox execution attach` report when a reconnect resumes after output the server no longer retains. While the client stays connected, the command runs at the client's pace, including when a pager pauses reading. If output is missing: * stdout keeps only the bytes received before the gap; * the retained tail goes to stderr between an `aient: output incomplete: bytes FROM-TO lost; ...` header and an `aient: output incomplete: end of retained tail` line; and * the CLI exits with status **74** when it receives the terminal result. The header reports the command's own outcome, including its exit code when known. With `--json`, the CLI emits an `execution_output_gap` event and also exits 74 at the terminal result. Treat the output as incomplete even if the command itself succeeded. Keep the retained tail for diagnosis, and use the execution ID with `status` or `wait` to inspect the command's outcome. Reattaching cannot restore bytes that are no longer retained; only rerun a command if repeating its effects is safe. ## `aient sandbox execution status` Copy the execution UUID printed by `sandbox exec` into a shell variable: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} printf "Paste execution UUID: " IFS= read -r EXECUTION_ID ``` Then read the current content-free state, repeating the original authority selectors: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox execution status \ --environment development \ --repository example/widget \ laptop-offload "${EXECUTION_ID}" ``` ## `aient sandbox execution attach` Attach to output from a known execution: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox execution attach \ --environment development \ --repository example/widget \ laptop-offload "${EXECUTION_ID}" ``` Add `--after-cursor CURSOR` to replay retained output after the last accepted cursor. Cursor acceptance is frame-atomic, so reconnect may repeat the last complete frame without creating an output gap or starting another command. Use `sandbox run --detach --keep` or `sandbox exec --detach` when the command must outlive its initiating client. ## `aient sandbox execution wait` Wait for the terminal outcome without receiving output: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --timeout 30m sandbox execution wait \ --environment development \ --repository example/widget \ laptop-offload "${EXECUTION_ID}" ``` Add `--json` for structured state. A terminal result can report a known exit code, cancellation, or an unknown outcome; the CLI does not guess by replaying the command. ## `aient sandbox execution cancel` Explicitly cancel the exact execution: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox execution cancel \ --environment development \ --repository example/widget \ laptop-offload "${EXECUTION_ID}" ``` Cancellation does not delete the sandbox. ## Environment and repository selectors All four execution-observation commands accept: | Flag | Meaning | | - | - | | `--environment SLUG_OR_ID` | Re-authorise the customer development environment. | | `--repository OWNER/NAME` | Re-select verified repository authority. Requires `--environment`. | Use the same selectors as the original environment-capable `sandbox exec`. ## Explicit file commands File commands address an existing sandbox without an environment binding and an explicit path. They are separate from exact Git workspace synchronisation. They cannot carry the selectors required to address an environment-bound customer sandbox. ### `aient sandbox files put` Upload one file or directory into an absolute remote directory: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox files put laptop-offload ./report.json /workspace/artifacts aient sandbox files put laptop-offload ./fixtures /workspace/fixtures \ --exclude '**/*.generated' ``` Directory excludes support `/`-separated `*`, `**`, and `?` globs. The CLI rejects upload of the active access-token file, including hard links to that file. ### `aient sandbox files get` Download one explicit remote file: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox files get \ laptop-offload /workspace/artifacts/report.json ./report.json ``` Directory download is not part of this command. ### `aient sandbox files ls` List an absolute remote directory: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox files ls laptop-offload /workspace aient sandbox files ls laptop-offload /workspace --json ``` Without a directory argument, the CLI lists `/`. ### `aient sandbox files rm` Remove one explicit remote path: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox files rm laptop-offload /workspace/artifacts/report.json ``` > [!WARNING] > > `files rm` changes the remote filesystem immediately. Verify the sandbox and absolute path before running it. ## Agent worktree reads Use `agent worktree read` when you need one exact baseline from an Agent's existing attached repository worktree. It is read-only: it does not create a sandbox, change Agent ownership or retention, or edit the worktree. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient agent worktree read THREAD src/config.json \ --repository example/widget \ --output agent-baseline.json ``` `PATH` is relative to the named attached repository's worktree. The command accepts one regular, single-link file up to 64 KiB and records its raw bytes, type, mode, and SHA-256. It rejects symlinks, `.git` paths, protected paths, and larger files. The destination must be new. Its existing parent must be owned by you and not writable by group or others. Aient writes the JSON artifact with mode `0600` and prints metadata rather than file content. The resulting artifact is a historical snapshot of the exact admitted binding, not a write grant or proof that the Agent still uses that worktree. The command needs both `aient.agent.read` and `aient.sandbox.files` grants, plus a CLI and server version that support the reader. If the binding changed, request a new read instead of treating an older artifact as current. ## Workspace mutation fencing Workspace reads and commands participate in a shared fence; sync activation and direct workspace mutation require the exclusive form. A file command against a busy workspace currently fails closed rather than waiting. If you see a workspace-busy response: 1. Do not loop concurrent mutations. 2. Check or wait for the known execution using its ID. 3. Retry the file operation only after the reader or mutation has finished. Never interpret partial transfer progress as a completed file installation. Explicit `files put` remains bounded by the total `--timeout` duration. The progress-sensitive inactivity budget described for `sandbox sync` does not apply to arbitrary file uploads. # Retained sandbox lifecycle Source: https://docs.aient.ai/ai-tools/cli/retained-sandboxes Create, find, wait for, enter, inspect, and delete retained Aient sandboxes. # Retained sandbox lifecycle Retain a sandbox when you want to run several commands against one uploaded workspace. For an environment-bound customer sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run \ --name laptop-offload \ --keep \ --environment development \ --repository example/widget \ -- true aient sandbox list --environment development aient sandbox sync laptop-offload . \ --environment development \ --repository example/widget aient --timeout 15m sandbox exec laptop-offload \ --environment development \ --repository example/widget \ --workdir /workspace/repo -- pnpm test aient sandbox delete laptop-offload ``` The one-shot command prints the logical sandbox name (the `name` field in JSON). In Aient CLI 0.11.17, that environment-bound sandbox can be reused through selector-bearing `sandbox sync`, `sandbox exec`, and `sandbox execution`, found with environment-scoped `list`, inspected with environment-scoped `status`, `wait`, and `logs`, and removed by its owner with `delete`. Run `sync` to publish changed workspace state. A `sessionId` identifies the physical runtime and is not a command target. `files` and `shell` cannot reopen the sandbox. ## `aient sandbox list` List sandboxes visible to the selected profile: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox list aient sandbox list --environment development aient sandbox list --json ``` `--environment` applies customer environment authorisation before listing. `--json` is useful for scripts. ## `aient sandbox create` Create a retained sandbox without running a command: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox create --name laptop-offload --lease-seconds 21600 ``` | Flag | Meaning | | - | - | | `--name NAME` | Requested sandbox name. | | `--lease-seconds N` | Requested lease. Default `7200`. | | `--metadata KEY=VALUE` | Non-secret control-plane metadata entry. Repeatable. | | `--json` | Emit structured output. | | `--ssh-known-hosts auto\|none` | Bootstrap repository-scoped public SSH host trust for the sandbox user. Default `auto`; use `none` to opt out. | Metadata must never contain credentials or sensitive values. It cannot override server-owned organisation, actor, environment, or repository identity. The default Git transport bootstrap uses only hosts selected by the current repository's `origin`, `upstream`, and root `.gitmodules`. It merges matching public host records into the guest and, for GitHub, configures the sandbox user to use scoped HTTPS repository authority for SSH-form URLs. It does not copy private keys, SSH configuration, agents, sockets, or credentials. Installation restrictions produce a warning without turning successful sandbox creation into a failure. For customer work that needs an environment's authorised development capabilities, prefer `sandbox run --environment ... --keep` or supply `--environment` on later `exec` and execution-observation operations. ## `aient sandbox status` Show the current state of a sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox status laptop-offload aient sandbox status laptop-offload --json aient sandbox status laptop-offload --environment development ``` Repeat the original `--environment` selector for an environment-bound customer sandbox. ## `aient sandbox wait` Wait until a sandbox becomes ready: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --timeout 10m sandbox wait laptop-offload aient --timeout 10m sandbox wait laptop-offload --environment development ``` | Flag | Meaning | | - | - | | `--poll-interval DURATION` | Status polling interval. Default `500ms`. | | `--json` | Emit structured output. | The global `--timeout` limits the wait. Repeat the original `--environment` selector for an environment-bound customer sandbox. ## `aient sandbox exec` Run a supervised command in an existing sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --timeout 15m sandbox exec laptop-offload \ --environment development \ --repository example/widget \ --workdir /workspace/repo -- pnpm test ``` The CLI prints a stable execution ID to stderr before dispatch: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Execution: 12345678-1234-4123-8123-123456789abc ``` Keep the sandbox name and execution ID if you may need to inspect or reconnect to the live execution. | Flag | Meaning | | - | - | | `--workspace PATH` | Synchronise a local checkout before running the command. | | `--workdir ABSOLUTE_PATH` | Working directory. Default `/workspace`. | | `--env KEY=VALUE` | Non-secret environment value. Repeatable. | | `--environment SLUG_OR_ID` | Authorise use of customer development capabilities. | | `--repository OWNER/NAME` | Select verified repository authority for provider-capable execution. | | `--shell COMMAND` | Explicit shell command. | | `--stdin` | Read an explicit shell command from standard input. | Exact argv, `--shell`, and `--stdin` are mutually exclusive command forms. Repository authority requires `--environment`. `sandbox exec` performs bounded same-execution transport recovery. It never replays an execution that may already have started. ## `aient sandbox docker bootstrap` Start a tmpfs-backed Docker daemon in a retained sandbox when a workflow needs Docker across more than one supervised command: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --timeout 30m sandbox docker bootstrap laptop-offload \ --environment development \ --repository example/widget ``` The daemon runs as a detached supervised execution from `/workspace/repo`. On compatible servers, it keeps live workspace consistency, so the daemon remains usable while a workspace sync publishes a new revision. `--timeout` bounds the daemon lifetime; the sandbox's retention policy still applies. Use `--reset` only to replace an existing daemon. It erases Docker and containerd data before preparation. If the CLI finds a ready daemon without a known live-consistency contract, it reports `docker ready` and tells you to rerun with `--reset` or recreate the sandbox before relying on sync. An incompatible server rejects bootstrap before changing Docker state. Keep the printed execution ID if bootstrap does not complete so you can inspect its terminal state instead of starting another daemon. ## `aient sandbox shell` Open a new interactive PTY shell in a sandbox without an environment binding: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox shell laptop-offload ``` Every invocation opens a new shell. Shell reattachment, history replay, and capability-bearing environment or repository flags are not available, so `shell` cannot address an environment-bound customer sandbox. Use `sandbox exec --environment ...` for authorised non-interactive operations. ## `aient sandbox logs` Read a bounded recent log snapshot from a sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox logs laptop-offload aient sandbox logs laptop-offload --tail-lines 500 aient sandbox logs laptop-offload --json aient sandbox logs laptop-offload --environment development ``` This is a sandbox runtime log snapshot, not stored command stdout/stderr history. Repeat the original `--environment` selector for an environment-bound customer sandbox. ## `aient sandbox delete` Delete a sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox delete laptop-offload ``` Delete retained sandboxes when you are finished. A sandbox lease is not a substitute for explicit cleanup. # Run and synchronise workspaces Source: https://docs.aient.ai/ai-tools/cli/run-and-sync Run an exact local checkout remotely and synchronise Git and explicit extra-file layers. # Run and synchronise workspaces Workspace transfer and repository authority are separate: * **Workspace source** is the exact local checkout or linked worktree transferred into the sandbox. * **Repository authority** is the server-verified GitHub repository against which brokered provider credentials and repository-scoped policy may be used. You can upload and test a workspace without GitHub authorisation. Pushing a branch, creating a pull request, or using repository-scoped capabilities requires a verified repository binding. ## `aient sandbox run` Create a sandbox, optionally transfer a workspace or upload, run one command, and delete the sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development -- pnpm test ``` With `--environment`, `--workspace` defaults to the current directory. With `--workspace`, the remote directory defaults to `/workspace/repo`, and the command runs there unless `--workdir` overrides it. Use exact argv after `--`: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development -- go test ./... ``` Use `--shell` when shell syntax is required. Aient CLI 0.11.26 and later use Bash for `--shell` and `--stdin`; arguments after `--` remain literal: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --shell 'pnpm lint && pnpm test' ``` Or read the shell body from standard input: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} printf '%s\n' 'pnpm lint && pnpm test' | aient sandbox run --environment development --stdin ``` ### Workspace and authority flags | Flag | Meaning | | - | - | | `--environment SLUG_OR_ID` | Select an authorised customer development environment. | | `--workspace PATH` | Synchronise a local Git checkout or linked worktree. | | `--repository OWNER/NAME` | Explicit repository selector for disambiguation and provider access. | | `--include GLOB` | Add an untracked or ignored repository-relative file layer. Repeatable. | | `--exclude GLOB` | Remove matching paths from the explicit non-Git layer. Without `--include`, selection starts from every ignored and untracked path. Repeatable. | | `--remote-dir ABSOLUTE_PATH` | Select a replaceable remote child directory for V1/V2 activation. V3 is fixed at `/workspace/repo` and rejects a custom root; never use the mounted `/workspace` root. | | `--submodules gitlinks\|selected` | Choose an explicit submodule policy. `gitlinks` omits all child content; `selected` transfers only repeatable `--submodule` repositories. | | `--submodule PATH` | Select one repository-relative Gitlink with `--submodules=selected`. Repeatable; nested selections require every selected ancestor. | | `--ssh-known-hosts auto\|none` | Bootstrap repository-scoped public SSH host trust before synchronization and execution. Default `auto`; use `none` to opt out. | Repository candidates are inferred from sanitised `origin` and `upstream` remotes when possible. Embedded remote credentials are not transmitted or displayed. If exactly one authorised repository matches, Aient binds it automatically. If none match, ordinary offload may continue without GitHub access. Use `--repository` when candidates are ambiguous. Changing a local remote cannot grant repository access. Every provider operation is re-authorised against the authenticated organisation, active GitHub App installation, and current repository permissions. ### Git SSH trust bootstrap By default, `sandbox run` selects SSH hosts only from the chosen repository's `origin`, `upstream`, and root `.gitmodules`. It copies matching public records already present in the host user's standard `known_hosts` files and atomically merges them into the sandbox user's `~/.ssh/known_hosts`. Existing unrelated sandbox entries are preserved. This never copies private keys, SSH configuration, an SSH agent/socket, credentials, or the complete host trust file. When `github.com` is selected, the sandbox user's Git configuration also rewrites the common GitHub SSH URL forms to HTTPS, where an authorised repository-capability-bearing command can use Aient's request-local scoped credential. Other SSH hosts receive trust only; they still need their own sandbox-available authentication. If local trust is absent or the sandbox runtime cannot install it, Aient prints a warning and continues creating the sandbox. Use `--ssh-known-hosts=none` when repository policy requires the image-provided trust state to remain unchanged. ### Selected nested repositories The default workspace policy rejects initialized submodules instead of silently transferring or omitting their checked-out bytes. If the command does not consume child repository content, use the explicit Gitlink-only policy: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --submodules=gitlinks -- pnpm test ``` If the command consumes a nested repository, select only that dependency: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development \ --submodules=selected \ --submodule apps/aient-docs \ -- pnpm --filter aient-app build ``` The same flags work with `sandbox sync`: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload . \ --submodules=selected \ --submodule apps/aient-docs ``` Selected mode publishes one atomic `/workspace/repo` containing the exact root Git state and only the explicitly selected child snapshots. Every unselected Gitlink remains an empty ordinary boundary, even when its local checkout is initialized or dirty; those bytes are not transferred, preserved, or treated as implicit input. Each selected child must be an ordinary initialized repository, clean and at the exact final parent-index Gitlink OID, with complete local history and no replacement refs. To select a nested descendant, repeat `--submodule` for the complete ancestor closure, using canonical paths from the top-level repository. Aient does not recursively discover descendants. The first selected protocol generation accepts at most 64 selected repositories and 512 Gitlink boundaries across the selected tree. `--submodule` without `--submodules=selected`, selected mode without a path, unsafe or duplicate paths, incomplete ancestry, and a custom remote root all fail before publication. Selected mode is fixed to `/workspace/repo`. A retained sandbox whose reader predates selected-submodule support returns `workspace_layout_selected_submodules_unavailable`; recreate or upgrade it rather than retrying with a broader transfer policy. ### Command and lifecycle flags | Flag | Meaning | | - | - | | `--workdir ABSOLUTE_PATH` | Remote working directory. | | `--env KEY=VALUE` | Non-secret environment value. Repeatable. | | `--shell COMMAND` | Run an explicit remote shell command instead of argv. | | `--stdin` | Read the explicit shell command from standard input. | | `--keep` | Retain the created sandbox after completion or failure. | | `--lease-seconds N` | Requested sandbox lease. Default `7200`. | | `--name NAME` | Requested sandbox name. | | `--metadata KEY=VALUE` | Non-secret control-plane metadata entry. Repeatable. | | `--download REMOTE=LOCAL` | Download an explicit file before cleanup. Repeatable. | `--upload PATH` uploads a non-Git file or directory instead of synchronising Git state. It excludes `.git` and `node_modules` by default. Do not combine `--upload` and `--workspace`. `--env` and `--metadata` are non-secret inputs. Metadata is ordinary control-plane state: never put tokens, credentials, or sensitive values in it. It cannot override server-owned organisation, actor, environment, or repository identity. Environment and GitHub credentials are injected only into an authorised sandbox process. Raw credentials are not returned as OAuth, profile, operation-result, metadata, or workspace-revision fields, and generic file APIs do not project mounted secrets. The CLI does not retain normal command output. An authorised child can nevertheless deliberately print or copy a mounted secret; stdout and stderr are returned verbatim to the connected caller, child-created files can contain those bytes, and either may be captured by its terminal or agent transcript. Avoid `env`, `printenv`, shell tracing, or echoing `GH_TOKEN`. > [!NOTE] > > Use `sandbox run --detach --keep` when a command must outlive the initiating client. Keep its execution ID so you can inspect, attach to, wait for, or cancel that same command. See [Executions, output, and files](/ai-tools/cli/executions-and-files). ## `aient sandbox sync` Synchronise exact local Git state into an existing sandbox: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload . ``` The default remote workspace is `/workspace/repo`. For an environment-bound customer sandbox, repeat the original environment and repository selectors: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload . \ --environment development \ --repository example/widget ``` Use the logical sandbox name, not a physical runtime `sessionId`. Aient CLI 0.11.22 reuses shared Git history for eligible native V3 workspaces during `sandbox sync` and `sandbox exec --workspace`. A workspace-backed command starts only after activation succeeds. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload . \ --include 'fixtures/local-only/**' \ --exclude '**/*.generated' \ --remote-dir /workspace/repo ``` The synchronised state includes the selected worktree's exact HEAD, index, tracked changes, and selected explicit extra files. Include and exclude globs use repository-relative `/` separators and support `*`, `**`, and `?`. Excludes filter only the explicit non-Git layer and cannot remove tracked Git state. With one or more `--include` patterns, they subtract from those selected paths. With no `--include`, exclude-only mode starts from the complete ignored and untracked inventory, then subtracts every matching path. This broad mode has no implicit cache or secret exclusions, so explicitly exclude `.env` files, credentials, private keys, dependency caches, and other unwanted local content. Raw `--upload` and `files put --exclude` have their own file-transfer filters. Git activation atomically replaces the destination directory. `--remote-dir` must therefore name a replaceable child on the workspace filesystem, such as `/workspace/repo` or `/workspace/alternate-repo`, never the mounted `/workspace` root itself. Raw upload may target `/workspace` because it does not use Git-directory activation. ### Re-sync at the same commit From Aient CLI 0.11.31, a sync does nothing when the local capture and remote workspace are unchanged. If the remote workspace has changed, the sync replaces it with the selected local state. If the CLI cannot check for changes, it performs a full sync. Save remote edits you need before synchronising. ### Keep installed pnpm dependencies Use `--preserve-node-modules` to keep existing pnpm dependency directories across a sync: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload . \ --environment development --repository example/widget \ --preserve-node-modules ``` The flag is available from Aient CLI 0.11.31 on `sandbox sync` and `sandbox exec --workspace`. It selects existing `node_modules` directories at the repository root and in `pnpm-workspace.yaml` packages, outside submodules. It does not install or update dependencies; run your package installation command when manifests or the lockfile change. Preservation requires ordinary remote directories and a server that supports explicit path ownership. The CLI reports directories skipped because they overlap untracked files selected for upload; resolve that overlap before retrying if you need to keep those dependencies. ### Atomic activation and the workspace fence Aient constructs and verifies a candidate before activating it as `/workspace/repo`. V3 is selected from an explicitly advertised capability and publishes an immutable full-snapshot or retained-delta candidate. CLI 0.11.31 can recover a failed Git-cache transfer by synchronising directly; it still waits for verified activation before starting your command. Activation replaces the live child on the same filesystem while holding the exclusive workspace fence; the `/workspace` mount point itself is not replaced. `sandbox exec`, `sandbox shell`, and workspace reads hold the shared fence. A sync waits before activation rather than replacing the live tree under an admitted command. Direct file operations currently fail with a busy response instead of waiting. Do not start a second workspace mutation while a sync is active. Wait for the real terminal prefix: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Synchronized Git HEAD 0123456789ab to sandbox-name:/workspace/repo ``` When an explicit non-Git layer is present, the CLI prints a second `Synchronized N explicit path(s) ...` line. Partial transfer progress is not completion. If another program invokes the CLI, it must preserve the child process's complete lifecycle result—including a running session handle, final exit status, and terminal output. Completion of an outer wrapper is not evidence that the CLI child has exited. Workspace archive uploads treat `--timeout` as an inactivity budget. The budget resets while source bytes continue to flow and when the server begins its response, so a healthy large transfer may exceed five minutes in total. A stalled upload or response still fails with a deadline-classified error. For a slow connection, increase the allowed inactivity gap: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --timeout 20m --verbose sandbox sync laptop-offload . ``` `--verbose` prints safe request correlation and detailed progress; it does not print credentials. An interrupted upload does not resume from its last reported byte. See [interrupted sync recovery](#recover-from-an-interrupted-workspace-sync) for automatic retries and what to do when a sync stops. ## Experimental selected-file live sync Aient CLI 0.11.23 adds two experimental modes for watching selected local files in an existing sandbox. Use them when you want to send source edits without publishing a complete Git workspace on every save. Start with a running sandbox whose workspace has already been synchronised. For `--live-stream`, use a compatible native V3 sandbox with an attested workspace and customer shell permission. Pass the actual remote worktree path, with existing parent directories for every selected file: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload /absolute/local/repository \ --environment development --repository example/widget \ --live-stream --remote-dir /workspace/repo \ --live-path src/app.ts ``` Repeat `--live-path` to select up to 128 existing regular files, each at most 32 MiB. Paths are relative to the local repository. The usual Git and explicit extra-file selection rules apply; Git control paths, credentials, symlink sources, and submodules are rejected. The initial upload overwrites the selected remote files. Later saves replace each file atomically, including ordinary editor saves that replace the local file. Identical captured content is skipped. Each acknowledgement confirms that file's byte count and SHA-256; it does not confirm application readiness or browser hot reload. The stream holds a shared workspace fence, so full workspace activation and exclusive file uploads remain blocked until it shuts down. Press Ctrl-C to request shutdown. If the CLI prints `termination UNVERIFIED`, the remote helper and fence may still be active. Starting another watcher does not recover that session and overwrites the selected files again. The reported session and process IDs are diagnostic information, not permission to terminate a process. To send selected files through the files API instead, replace `--live-stream` with `--live`. This mode requires an attested existing workspace and customer files permission. Both modes require explicit `--remote-dir` and `--live-path`; choose only one mode. Live sync does not transfer Git metadata, protect concurrent edits, propagate deletions or renames, select newly created files, or transfer mode changes. It provides neither an atomic update across multiple files nor reconnect recovery. Errors stop the watcher. Ending the watcher leaves the sandbox running, subject to its existing lifetime limits. ### Live sync through supervised execution Aient CLI 0.11.26 adds an experimental exec transport for a compatible existing customer workspace. Start the watcher before starting a long-running application: setup needs an exclusive workspace fence. After setup, writes share the fence with ordinary application execution. ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox sync laptop-offload /absolute/local/repository \ --environment development --repository example/widget \ --live --live-transport exec --remote-dir /workspace/repo \ --live-path src/app.ts ``` Use the environment and repository that own the sandbox, and set `--remote-dir` to its actual worktree path. Each selected file must already exist remotely as a regular file with one hard link, with existing ordinary parent directories. This transport limits each captured file to 64 KiB and preserves the remote file mode. Unsupported runtimes and operator profiles reject this transport; there is no automatic transport fallback. An exact path, byte count, SHA-256 acknowledgement, and successful command completion confirm each write. If the CLI reports `write UNCONFIRMED`, remote bytes may have changed. The watcher stops without retrying; inspect and reconcile the selected files before restarting. Each write command has a 30-second limit, and the sandbox's existing expiry still applies. The overwrite and recovery limits above also apply here: delivery does not confirm application readiness or browser hot reload. Use [Agent checkout sync](/ai-tools/cli#sync-selected-files-into-an-agent-checkout) for a one-shot update to an Agent-owned checkout. ## Recover from an interrupted workspace sync From Aient CLI 0.11.31, a failed Git-cache sync can continue through a direct workspace transfer. The CLI reports the fallback on stderr and retains the cache operation until the direct sync succeeds. If both fail, it reports both errors. Cancellation, authorisation failures and a replaced sandbox still stop the operation. While the server reports that workspace preparation is busy, the CLI follows its retry hint for up to 15 minutes across preparation, upload, finalisation and activation. No new flag is required. A command starts only after the workspace is active. Aient CLI 0.11.28 automatically retries supported transient network failures and incomplete responses during workspace transfers. Use the existing `sandbox sync`, workspace-backed `sandbox run`, or `sandbox exec --workspace` command; no new flag is required. Retries keep the originally captured files and obtain fresh authorisation for the same target and operation. Candidate uploads replay the captured archive. Numbered Git-cache uploads replay only the affected part, so a lost response does not restart the whole pack. Retries are bounded. Cancellation and authorisation failures stop the transfer. Partially streamed downloads and other mutations are not automatically replayed after an ambiguous failure. Selected-file Agent sync and experimental live sync keep their existing recovery limits. Workspace operations live in `~/.aient/workspace-operations` only while the command that started them runs. When a `sync`, `run` or `exec --workspace` operation ends, whether it activated, failed, was rejected or was cancelled, nothing of it is kept there. An operation that did not activate first gets one best-effort discard on the sandbox, bounded to 10 seconds and sent even after Ctrl-C; if that discard fails, the CLI prints one line and the sandbox expires the operation. A failed sync never starts the command, and `sandbox run` deletes its sandbox after a failed sync unless you pass `--keep`. When a sync starts, the CLI prints: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Workspace operation OPERATION_ID started; if it is interrupted, rerun this command to sync again. ``` If a sync fails or is interrupted, rerun the same command. The rerun syncs the worktree as it is now, including edits made since the interrupted attempt; nothing is resumed. Only a killed CLI leaves a record behind. The next sync or `sandbox workspace list` removes every record no running CLI holds, including copies that earlier releases kept for resume. `workspace list` shows the store without authentication or network access: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox workspace list ``` While a record is still present, you can check its operation on the sandbox, or release an uncommitted publication: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox workspace status "${OPERATION_ID}" aient sandbox workspace discard "${OPERATION_ID}" ``` Discard does not remove an activated or activation-pending workspace. On a transport failure, the CLI keeps the local record so you can retry; it removes it after the server proves that the exact operation was discarded, was already absent, or its sandbox has been deleted. # CLI troubleshooting and current limits Source: https://docs.aient.ai/ai-tools/cli/troubleshooting Resolve authentication, environment policy, repository, transfer, and live-execution errors. # CLI troubleshooting and current limits ## `development environment is not enabled for CLI workloads` Example: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} HTTP 403: development environment is not enabled for CLI workloads ``` The selected environment exists, but its administrator has not allowed authenticated customer CLI workspaces to use its sandbox-exportable development capabilities. Ask an organisation administrator to open the environment's **Sandbox** settings and enable customer CLI development. Then repeat the command with that environment's actual slug: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run --environment development -- pnpm test ``` This is a policy denial before sandbox creation. ## Profile is not logged in Example: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} profile "client-a" selected from project is not logged in ``` The nearest `.aient/config.yaml`, `AIENT_PROFILE`, or `--profile` selected a profile that has no local login: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth login --profile client-a aient auth status --profile client-a ``` Use `aient auth profiles` to inspect local profile names. ## Project organisation mismatch The `organisation` in `.aient/config.yaml` is an assertion. If OAuth resolves another organisation, Aient fails before sandbox creation. Check the selected profile: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient auth status aient auth profiles ``` Then correct the non-secret project selector or log into the intended profile. Do not remove the assertion merely to force execution against the wrong customer context. ## Repository is absent or ambiguous A local Git remote is only a selector. If no authorised repository matches, ordinary workspace offload may continue with `GitHub access: none`. Provider operations such as branch push and `gh pr create` require verified repository authority. When more than one remote is plausible, disambiguate: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient sandbox run \ --environment development \ --repository example/widget \ -- pnpm test ``` Changing `origin` cannot grant access to another organisation's private repository. ## `sandbox_allocation_terms_conflict` Aient CLI 0.11.29 reports this HTTP 409 error when a sandbox name already has an allocation with a different size, CPU, memory, or environment. The allocation can belong to a live sandbox or a creation attempt refused in the last few minutes. The failed request creates nothing. Retry with the original options, choose another `--name`, or [delete the existing sandbox](/ai-tools/cli/retained-sandboxes#aient-sandbox-delete) before recreating it if you no longer need its contents. Repeating the conflicting request unchanged will not resolve the error. ## `workspace busy` An admitted command or workspace read holds the shared workspace fence, while sync and file mutations require exclusive access. * Wait for the current execution with `sandbox execution wait`, repeating its original environment and repository selectors. * Avoid starting multiple syncs or direct workspace mutations. * Retry only after the known reader or mutation finishes. The CLI never replays an arbitrary command that may already have started. ## Transfer shows progress but the workspace is absent A line such as `Transferred workspace archive: 56.0 MiB / 448.4 MiB` is progress, not completion. Successful sync ends with an explicit `Synchronized ...` line and a terminal process result. If you invoke `aient` from an agent, build tool, or wrapper: * preserve the child result object, including a running session handle; * continue polling that exact handle until a terminal exit status; * keep draining stdout and stderr; * do not treat completion of the outer wrapper as completion of the CLI; and * do not mutate the target workspace while the upload is active. Since Aient CLI 0.8.2, `sandbox sync` resets its timeout while upload bytes continue to flow and when the server begins responding. If a current transfer fails despite continuing progress, check `aient version`; releases before 0.8.2 applied the default five-minute budget to the whole HTTP request. For an unusually slow connection, increase the permitted inactivity gap: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} aient --timeout 20m --verbose sandbox sync laptop-offload . ``` Reuse an environment-bound customer sandbox with `sandbox sync SANDBOX . --environment ENV --repository OWNER/REPO`, repeating its original selectors. An interrupted upload does not continue from its last reported byte. Rerun the same command: it syncs the worktree as it is now. See [interrupted sync recovery](/ai-tools/cli/run-and-sync#recover-from-an-interrupted-workspace-sync). ## Connection ended during `sandbox exec` `sandbox exec` prints the execution ID before dispatch and performs bounded transport recovery for that same execution. If the client still loses its connection: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} printf "Paste execution UUID: " IFS= read -r EXECUTION_ID aient sandbox execution status \ --environment development \ --repository example/widget \ laptop-offload "${EXECUTION_ID}" aient sandbox execution attach \ --environment development \ --repository example/widget \ laptop-offload "${EXECUTION_ID}" ``` Use `attach --after-cursor CURSOR` to replay supervisor-retained output after the last accepted cursor, then follow live output. Retention is bounded. Use `execution wait` when you need only the terminal outcome. If you see `output incomplete` or exit status 74, follow the [incomplete-output guidance](/ai-tools/cli/executions-and-files#incomplete-output-after-a-reconnect). ## Portable access token expired Imported access-only credentials are never refreshed. Export another from a refresh-backed profile or log in on the target machine: Use the unique temporary-directory and cleanup pattern in [Authentication and project profiles](/ai-tools/cli/authentication). Delete the old access-only file. Do not copy refresh tokens or keychain data. ## A retained customer sandbox rejects `sync`, `files`, or `shell` An environment-bound sandbox created with `sandbox run --environment ... --keep` requires the original environment and repository selectors on later `sync`, `exec`, and `execution` operations. `status`, `wait`, and `logs` require the original `--environment` selector. Upgrade an older CLI before retrying `sync`. `files` and `shell` cannot address an environment-bound sandbox. ## Current unsupported behaviour * `sandbox shell` cannot reattach and has no history replay. * Interactive shells cannot request environment or repository capabilities. * Environment-bound customer sandboxes cannot use `files` or `shell`. * `files get` downloads a file, not a directory. For other failures, rerun with `--verbose`. It prints safe correlation metadata without printing OAuth, environment, or provider credentials. # Customer MCP connections Source: https://docs.aient.ai/ai-tools/customer-mcp-connections Attach approved tools from your remote MCP servers to Sparkles in one Aient environment. # Customer MCP connections Connect a remote MCP server that your organisation operates or trusts, review its tools, and let Sparkles use an approved subset in one Aient environment. This feature makes Aient the MCP **client**. It is different from connecting Codex, Claude, or another MCP client to Aient's own `https://aient.ai/mcp` server. To talk to Aient from your assistant, see [Start a conversation through MCP](/ai-tools/mcp-conversations). > [!NOTE] > > Customer connections are scoped to one environment. A Production connection is not visible to > tasks in Development, and there is no organisation-wide fallback. ## Before you start Your server must expose MCP Streamable HTTP at a public HTTPS URL on port 443. Aient supports: * no authentication; * a bearer token; or * one API-key header, such as `X-API-Key`. Private-network addresses, redirects, query strings, URL fragments, URL credentials, arbitrary ports, local `stdio` processes, and arbitrary header maps are not supported. You must be an organisation owner or administrator. Keep the remote server's tools safe for the autonomous read access you intend to approve. ## Connect through the UI 1. Open **Settings → Environments** and choose the environment. 2. Select **MCP connections**, then **Add MCP connection**. 3. Enter a stable alias, display name, HTTPS endpoint, and authentication type. New connections are disabled. 4. If authentication is required, enter the credential. Aient stores it in the organisation vault and never displays it again. 5. Select **Discover tools**, review the server-derived list, select only the tools you trust for autonomous read use, and confirm the approval. 6. Enable the connection. Approval and enablement are separate. A connection becomes ready only when it is enabled, has a current approved manifest, and has a valid credential when its authentication type requires one. ## What Sparkles receives At the start of a new task, Aient resolves the ready connections for that task's exact organisation and environment. Sparkles receives only a bounded snapshot containing approved tool names, descriptions, and schemas. It does not receive the endpoint, credential, secret reference, or a live MCP client. Before every tool call, Pulse verifies that the connection is still enabled and that its revision and approved manifest still match the task snapshot. A changed, disabled, or deleted connection fails closed. The remote call then runs once through Aient's protected egress path. Aient does not automatically retry an ambiguous tool call. ## Readiness states | State | Meaning | Next action | | - | - | - | | `ready` | Enabled with a current approval and required credential | Start a new Sparkles task in this environment. | | `disabled` | Configuration may be valid, but agents cannot receive it | Enable after reviewing the current approval. | | `credential_missing` | Bearer or API-key auth has no usable credential | Attach or replace the credential. | | `approval_missing` | No tools have been approved | Discover, select, and approve tools. | | `approval_stale` | Endpoint or authentication configuration changed after approval | Discover and approve the current manifest again. | Readiness is derived from the current configuration; it is not a separate status that can drift from the connection. ## Manage through the public API The OpenAPI 3.1.1 document is available at [`https://aient.ai/api/v1/openapi.json`](https://aient.ai/api/v1/openapi.json). Use an Aient API key provisioned with `mcp_connections:read` for list/get or `mcp_connections:manage` for lifecycle operations. The current API-key dialog does not expose these permissions, so prefer audience-bound Aient CLI OAuth for customer automation unless an owner/admin has provisioned the key through the authenticated key API. Browser session cookies are not accepted on `/api/v1`. | Operation | HTTP request | | - | - | | List / create | `GET` / `POST /api/v1/environments/{environmentId}/mcp-connections` | | Get / update / delete | `GET` / `PATCH` / `DELETE /api/v1/environments/{environmentId}/mcp-connections/{connectionId}` | | Discover | `POST .../{connectionId}/discoveries` | | Approve tools | `POST .../{connectionId}/tool-approvals` | | Enable or disable | `PUT .../{connectionId}/enablement` | | Set or rotate credential | `PUT .../{connectionId}/credential` | | Preview deletion | `POST .../{connectionId}/deletion-previews` | Create and every mutation require an `Idempotency-Key`. Mutations of an existing connection also require `If-Match` with the quoted numeric revision returned by the latest read, for example `If-Match: "4"`. Re-read after a `412` revision conflict instead of overwriting newer changes. The credential request body is write-only. Responses contain `endpointOrigin` and `auth.credentialReady`, never the full stored endpoint path, vault identifier, or credential. ### Example: create a disabled connection ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} curl --request POST \ "https://aient.ai/api/v1/environments/${AIENT_ENVIRONMENT_ID}/mcp-connections" \ --header "Authorization: Bearer ${AIENT_API_KEY}" \ --header "Content-Type: application/json" \ --header "Idempotency-Key: setup-customer-tools-01" \ --data '{ "alias": "customer-tools", "displayName": "Customer tools", "endpointUrl": "https://mcp.example.com/mcp", "auth": { "kind": "bearer" } }' ``` The response is a disabled, credential-free connection at revision 1. Attach the credential before discovery. ## Manage through the Aient CLI The CLI command family is: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} aient environment mcp list|get|create|update|discover|approve|enable|disable|delete aient environment mcp credential set ``` Every command requires `--environment` with the environment UUID. Use `--json` for structured automation. Lists support `--page-size`, `--cursor`, and `--all`. Mutations require `--revision`; the CLI generates and reports an idempotency key when you do not provide one. Credentials are accepted only from stdin: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} printf '%s' "${CUSTOMER_MCP_TOKEN}" | \ aient environment mcp credential set mcp_... \ --environment "${AIENT_ENVIRONMENT_ID}" \ --revision 1 \ --stdin ``` > [!WARNING] > > The customer MCP command family was merged after Aient CLI `0.10.2` and will be included in the > next CLI release. If `aient environment mcp --help` is unavailable, use the UI, public API, or > Aient MCP management tools until you upgrade. An OAuth profile created before the MCP scopes were introduced must run `aient auth login` again. The CLI calls the public REST API; it does not send these commands through Aient's inbound MCP server. ## Manage through Aient MCP An agent connected to `https://aient.ai/mcp` can use: * `list_mcp_connections` and `get_mcp_connection`; * `create_mcp_connection` and `update_mcp_connection`; * `discover_mcp_connection_tools` and `approve_mcp_connection_tools`; * `set_mcp_connection_enabled`; * `preview_mcp_connection_deletion` and `delete_mcp_connection`; and * `prepare_mcp_connection_credential_upload`. Writes require `aient.mcp.write`. The management tools return concise text plus structured, redacted results. They never accept a reusable credential as model-visible input. For a credential, `prepare_mcp_connection_credential_upload` creates a short-lived, single-use browser action bound to the same user, environment, connection, and revision. Open that action as the signed-in human owner, then let the MCP client poll the same request. Never paste the credential into chat. ## Updating, rotating, and deleting * Disable a connection before changing its endpoint or authentication configuration. Those changes clear the old approval. * Credential rotation validates the candidate before replacing the live secret. A failed candidate leaves the current credential in place. * Disable before deletion. Preview deletion first, inspect the approved-tool and credential effects, then confirm with the short-lived receipt. * A configuration change does not rewrite a task already in progress. Its schema stays stable, but a later call fails if the connection no longer matches; a new task resolves the new configuration. ## Current limits An environment may have at most 8 ready connections and 64 ready customer tools. One connection may approve at most 128 tools. Aient also bounds schema size and depth, descriptions, task snapshots, remote responses, and normalized tool results. If discovery exceeds a limit, reduce the server's exposed tool catalog before trying again. ## Troubleshooting | Problem | Resolution | | - | - | | `403` from the CLI or API | Sign in again for the new OAuth scopes, or use an API key already provisioned with the required MCP permission. Confirm the caller is an owner/admin in the environment's organisation. | | Endpoint rejected | Use canonical public HTTPS on port 443 with no query, fragment, redirect, URL credentials, or private address. | | `revision_conflict` / HTTP `412` | Get the connection again and repeat the deliberate change using its current revision. | | `manifest_changed` | Discover again, review the new manifest, and approve a fresh subset. | | `credential_required` or `remote_auth_failed` | Attach or rotate the credential through UI, API/CLI secret-safe ingress, or the MCP browser handoff. | | Connection enabled but absent from an old task | Start a new task. Existing tasks keep their task-frozen attachment snapshot. | # Kubernetes connections Source: https://docs.aient.ai/ai-tools/kubernetes-connections Let Aient's agents read one of your Kubernetes clusters through a connector you install, without giving Aient a Kubernetes credential. # Kubernetes connections A Kubernetes connection lets the agents in one Aient environment read a cluster you operate. You install a small connector in the cluster. It dials out to Aient over HTTPS, so the cluster needs no inbound access, and Aient never holds a Kubernetes credential. Every connection is **read-only**. When an agent runs `kubectl`, Aient's gateway checks the request first. The connector then mints a short-lived token for a dedicated viewer ServiceAccount and sends the request to your API server. Your cluster's own RBAC decides what that viewer can read, and your audit log records every request under it. > [!NOTE] > > A connection belongs to one environment. A cluster attached to Production is not visible to agents working in > Development. ## What agents can do Today, **Threads** use a connection. When a Thread's agent runs in a native Aient sandbox, Aient writes a kubeconfig with one context per enabled connection, named after the connection. The agent runs stock `kubectl`: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} kubectl --context prod get pods -n payments kubectl --context prod describe deployment checkout -n payments kubectl --context prod logs deployment/checkout -n payments --tail=200 ``` Aient's gateway admits only reads (`get` and `list`). It refuses: * every write, such as `apply`, `delete`, `scale` or `patch`; * streams: `--watch`, `logs -f`, `exec`, `attach`, `port-forward` and `proxy`. A refused request reaches neither the connector nor your cluster. Each `kubectl` invocation uses one connection. The connector mints a separate viewer token for each command; the token is valid for 10 minutes and is discarded when the command ends. Before a response leaves the cluster, the connector removes credential-shaped values from it, such as PEM blocks, known token formats, URL passwords and `last-applied-configuration`. ## Before you start * You are an owner or admin of the Aient organisation. * You can run `kubectl` against the cluster with rights to create a namespace, Secrets, ServiceAccounts, Roles, ClusterRoles and their bindings, a Deployment and a NetworkPolicy. Kubernetes lets you create and bind the viewer's ClusterRole only if you hold its permissions yourself, or have `escalate` and `bind`. * The machine where you install has Bash, `kubectl`, `curl` and an OpenSSL that can generate Ed25519 keys (`openssl genpkey -algorithm ed25519`). It must reach `https://aient.ai`. * Pods in the cluster can pull `ghcr.io/aient-ai/kubernetes-connector` and open outbound HTTPS to `kube-broker.aient.ai`. The connector also needs DNS and the Kubernetes API (TCP 443 or 6443). ## Choose the RBAC you declare When you attach a cluster, you declare the RBAC its viewer gets: one or more `Role` or `ClusterRole` documents in YAML or JSON. Aient merges their rules into one declaration. This declaration is the boundary: * the install manifest creates a ClusterRole with exactly these rules and binds it to the viewer; * after install, the connector measures what the viewer can really do; * Aient refuses to enable the connection if the viewer can do more than you declared, for example because another binding in the cluster also grants it access. Start from Aient's published read-only template, [`aient-viewer.yaml`](/reference/kubernetes-aient-viewer). It reads the common built-in and operator resources, and pod logs. It leaves out Secrets, and other resources whose contents can carry secret material. Remove any group you do not want agents to read. The declaration must not use YAML anchors or aliases (`&name` / `*name`). Aient refuses them so that a small upload cannot expand into a large declaration. Expand them before you paste. ### High-risk access If the measured viewer can read Secrets (`secrets get` or `secrets list`), the connection shows that access as high risk. You must name each high-risk capability when you confirm. Other dangerous rights, such as `exec`, `attach`, `port-forward`, proxies, impersonation, token creation, `escalate` and `bind`, never need confirming: the gateway refuses them whatever your RBAC grants. ## Attach a cluster ### In the Aient app 1. Open **Settings → Environments**, choose the environment, and select **Kubernetes**. 2. Select **Attach a cluster**. Enter a cluster name, such as `prod`, and paste the RBAC for the viewer. The name is lowercase letters, digits and hyphens, unique in the environment. Agents select the cluster by this name. 3. Select **Attach**. Aient shows the install manifest and the install command **once**. Download the manifest as `aient-connector.yaml` and copy the command, then select **I have saved them**. 4. On a machine where `kubectl` reaches the cluster, run the command in the directory that holds `aient-connector.yaml`. 5. Open the connection's **Read-back**. Within a few minutes, the state moves from *Waiting for the connector* to *Measuring the viewer*, then to *Ready to confirm*. 6. Review the read-back: the viewer's username, the cluster UID, and any high-risk access. Tick each high-risk item, then select **Confirm and enable**. The connection starts disabled and stays disabled until you confirm. ### Through Aient's MCP server An assistant connected to `https://aient.ai/mcp` with write scope can run the same flow: | Step | Tool | | - | - | | Attach and get the install | `create_kubernetes_connection` (`name`, `rbac`, optional `agentUses`) | | Read the state and the read-back | `get_kubernetes_connection`, `list_kubernetes_connections` | | Enable | `confirm_kubernetes_connection` with the `revision` and `declaredPolicyHash` you read, and every name in `highRiskToAcknowledge` | | Issue a new enrolment code | `rotate_kubernetes_connection_enrolment` | | Replace the declared RBAC | `set_kubernetes_connection_policy` | Confirmation is refused if the connection changed since you read it. Read it again and retry. ### With Helm or Kustomize The connector is also published as a Helm chart and a Kustomize tree in the public [aient-ai/helm-charts](https://github.com/aient-ai/helm-charts) repository. Use them when you want to render `aient-connector.yaml` yourself, for example to set values, scope the viewer to some namespaces, or deploy through Argo CD or Flux. 1. Attach the cluster as above. Declare the RBAC that the chart renders for your values; print it with `helm template ... --show-only templates/viewer.yaml`. 2. Render the chart for the connection into the file the install command applies: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} helm repo add aient https://aient-ai.github.io/helm-charts helm template kubernetes-connector aient/kubernetes-connector --version \ --namespace aient --set connection.id= > aient-connector.yaml ``` The same chart is at `oci://ghcr.io/aient-ai/charts/kubernetes-connector`. 3. Run the install command in the same directory. The chart never creates the key Secret `aient-connector-key`; the install command creates it and applies the rendered file. The chart's README lists its values, and the repository's README shows how to verify the signed releases. ## What the install command does The command is a Bash script. It carries the one-time enrolment code, and Aient keeps only a digest of that code. Treat the command as a secret until it has run. The script: 1. creates the namespace `aient` if it does not exist; 2. generates an Ed25519 key and stores it in the Secret `aient/aient-connector-key`. If that Secret already exists, the script reuses its key instead; 3. sends the **public** key with the enrolment code to `https://aient.ai/api/kubernetes-connections/enrol`; 4. applies `aient-connector.yaml`; 5. records the public key on the connector Deployment, so the connector restarts when the key changes. The private key never leaves your cluster's Secret. The script deletes its local copy when it exits. The manifest creates these objects in namespace `aient`: | Object | Purpose | | - | - | | ServiceAccount `aient-connector` | Runs the connector. Its only right is to mint tokens for the viewer. | | ServiceAccount `aient-kconn-` | The viewer: the identity every agent request uses. It mounts no token of its own. | | Secret `aient-kconn--anchor` | Empty. Every viewer token is bound to it. | | Role and RoleBinding `aient-connector-mint` | `create serviceaccounts/token`, for the viewer only. | | ClusterRole and ClusterRoleBinding `aient-kconn--viewer` | Your declared RBAC, bound to the viewer. | | ConfigMap `aient-connection` | The broker URL and the connection ID. | | Deployment `aient-kube-connector` | One non-root replica with a read-only root filesystem. | | NetworkPolicy `aient-kube-connector` | No ingress. Egress only to cluster DNS and TCP 443 and 6443. | The script can be run again. A repeat run after success changes nothing. A run interrupted at any step continues from where it stopped. ## Change, rotate and remove **Change the declared RBAC.** Select **Change RBAC** (or call `set_kubernetes_connection_policy`) and paste the new declaration. The connection is disabled at once. Aient returns a new manifest without a new enrolment code. Apply it with `kubectl apply -f aient-connector.yaml`, wait for the read-back, and confirm again. **Get a new enrolment code.** Select **New enrolment code** (or call `rotate_kubernetes_connection_enrolment`) when a code was lost or exposed, or to reinstall the connector. The old code stops working immediately. Run the new install command. An installed connector keeps its key; the new run reuses it. **Replace the connector's key.** Delete the key Secret, issue a new enrolment code, and run the new install command: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} kubectl -n aient delete secret aient-connector-key ``` The script generates a new key, enrols it, and restarts the connector with it. **Cut off access from the cluster.** Delete the anchor Secret. Every viewer token is bound to it, so all of them stop working within about 10 seconds, and the connector cannot mint new ones: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} kubectl -n aient delete secret aient-kconn--anchor ``` To remove the connector entirely, delete what the manifest created and the key Secret: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} kubectl delete -f aient-connector.yaml kubectl -n aient delete secret aient-connector-key ``` A connection whose connector is gone cannot serve any request. To stop agents from using a connection at once, change its RBAC: that disables it until you confirm again. ## Troubleshooting ### The state stays at *Waiting for the connector* The connector has not enrolled. Check that the install command finished without an error. If the enrol step answered `401`, the code was already used, replaced, or mistyped: issue a new enrolment code and run the new command. A `429` means too many enrolment attempts from your address (more than 10 in 10 minutes); wait and retry. ### The state stays at *Measuring the viewer* The connector enrolled but has not reached Aient. Check the pod and its logs: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} kubectl -n aient get pods -l app.kubernetes.io/name=aient-kube-connector kubectl -n aient logs deployment/aient-kube-connector --tail=100 ``` Look for an image pull error, a missing `aient-connector-key` Secret, or `broker session ended; reconnecting`. The last means it cannot hold a session with `kube-broker.aient.ai`: check egress to that host on TCP 443, including any NetworkPolicy or proxy of your own. The connector retries with backoff, waiting at most 30 seconds between attempts. ### *Viewer identity differs* (`identity_mismatch`) The connector measured a different identity from the viewer Aient expects, `system:serviceaccount:aient:aient-kconn-`. This happens when the manifest was edited (for example, the viewer ServiceAccount renamed), or when the install used a manifest from another connection. Apply the manifest for this connection unchanged. To get it again without a new code, use **Change RBAC** with the same RBAC. Then restart the connector so it measures again: ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}} kubectl -n aient rollout restart deployment/aient-kube-connector ``` ### *Exceeds the declared RBAC* (`exceeds_policy`) The viewer can do more than you declared. The read-back lists each extra grant. Remove the binding that grants it in the cluster, or declare it with **Change RBAC**. A connection that drifts beyond its declaration later is disabled. ### `kubectl` errors in a Thread | `kubectl` reports | Meaning | | - | - | | `this environment has no Kubernetes connection named "…"` (404) | No enabled connection with that name serves Threads in this environment. | | `this connection is read-only: Aient's Kubernetes gateway admits get and list` (403) | The command writes. | | `Aient's Kubernetes gateway refuses streams (…)` (403) | The command watches, follows, or opens a session. Drop `--watch` or `-f`. | | `this Kubernetes connection is not enabled and validated` (403) | Confirm the connection, or confirm it again after an RBAC change. | | `the Kubernetes connection answered unavailable: not_connected` (503) | The connector has no session with Aient right now. | | `the Kubernetes connection answered unavailable: hub_unreachable` (503) | Aient's connector hub is briefly unavailable, for example while it restarts. | The two `503` answers are transient while Aient restarts its connector hub: the connector reconnects on its own within about 30 seconds. Retry the command. If `not_connected` persists, check the connector as described above. # Send AWS CloudWatch Logs to Aient with Firehose Source: https://docs.aient.ai/aws/firehose Connect Amazon CloudWatch Logs to Aient through Amazon Data Firehose, so AWS application errors can become Aient problems and reviewed remediation PRs. # Send AWS CloudWatch Logs to Aient with Firehose Amazon CloudWatch Logs can stream log groups to an HTTPS endpoint through Amazon Data Firehose. Point Firehose at Aient and your AWS logs enter the same detection and remediation loop as SDK and Vercel telemetry. Aient accepts the standard CloudWatch Logs payload, stamps each event to the selected organisation and environment, groups error-class records into problems, and uses stack traces as evidence for root cause analysis and reviewed GitHub fix pull requests. > [!NOTE] > > This path ingests **CloudWatch Logs**. It does not replace OpenTelemetry traces. For distributed > trace context, keep using the [server telemetry quickstart](/quickstart/server) or a standard > OTLP client pointed at Aient. ## What you get * **CloudWatch logs in Aient.** CloudWatch `DATA_MESSAGE` records arrive as Aient log telemetry without changing application code. * **Problem detection from AWS errors.** Error- and fatal-level records can become Aient problems, especially when logs include a clear message, error name, and stack trace. * **Retry-safe delivery.** Firehose retries are deduplicated from CloudWatch event identity, so transient delivery failures should not inflate problem occurrence counts. * **Closed-loop remediation.** When Aient can connect the stack frame to a repository it can read, it investigates the root cause and opens a GitHub pull request for review. ## Prerequisites * An Aient account with an environment that should receive the logs. * AWS permissions to create or update: * an Amazon Data Firehose delivery stream, * IAM roles for Firehose and CloudWatch Logs, * CloudWatch Logs subscription filters, * an S3 bucket for failed Firehose deliveries. * One or more CloudWatch log groups you want Aient to monitor. ## Get your Aient Firehose endpoint In Aient, go to **Settings -> Environments**, open the target environment, and select **Drains**. Click **AWS Firehose**. Aient shows you, once: * **Endpoint URL** - the HTTPS URL for this Aient drain: ```txt theme={"theme":{"light":"github-light","dark":"github-dark"}} https://ingest.aient.ai/aws/firehose/ ``` * **X-Amz-Firehose-Access-Key** - the access key you paste into the Firehose HTTP endpoint destination. > [!WARNING] > > The endpoint URL and access key are credentials. Copy them before closing the dialog, store the > access key in a secret manager or sensitive Terraform variable, and never commit either value. > Aient stores only a digest of the access key, so it cannot be retrieved later. If you lose the access key, rotate the drain in Aient and update Firehose with the new value. Revoking the drain immediately stops accepting traffic for that endpoint. ## Set up Firehose in AWS Create an Amazon Data Firehose delivery stream with: | Setting | Value | | - | - | | Source | `Direct PUT` | | Destination | `HTTP endpoint` | | HTTP endpoint URL | The Aient endpoint URL | | Access key | The Aient `X-Amz-Firehose-Access-Key` | | Content encoding | Disabled / `NONE` | | Retry duration | Start with `300` seconds | | S3 backup | Failed data only | > [!WARNING] > > Keep Firehose HTTP request content encoding disabled. CloudWatch Logs already sends a > gzip-compressed payload inside each Firehose record. If Firehose gzips the whole HTTP request, > Aient rejects the payload. Then add CloudWatch Logs subscription filters for the log groups you want to monitor: 1. Create or choose an S3 bucket for failed Firehose deliveries. 2. Create an IAM role trusted by `firehose.amazonaws.com` with permission to write failed records to that S3 bucket. 3. Create the Firehose delivery stream using the Aient endpoint and access key. 4. Create an IAM role trusted by `logs.amazonaws.com` with permission to call `firehose:PutRecord` and `firehose:PutRecordBatch` on the delivery stream. 5. Add a CloudWatch Logs subscription filter to each log group: * destination: the Firehose delivery stream ARN, * role: the CloudWatch Logs role, * filter pattern: `""` for all logs, or a narrower CloudWatch Logs filter pattern. CloudWatch can send `CONTROL_MESSAGE` records to test reachability. Aient accepts and ignores those; they do not create logs or problems. ## Terraform example This example creates one Firehose stream and subscribes existing log groups to it. ```hcl theme={"theme":{"light":"github-light","dark":"github-dark"}} variable "aient_firehose_endpoint_url" { description = "Aient AWS Firehose endpoint URL from Settings -> Environments -> Drains" type = string } variable "aient_firehose_access_key" { description = "Copy-once X-Amz-Firehose-Access-Key from Aient" type = string sensitive = true } variable "cloudwatch_log_group_names" { description = "CloudWatch log groups to send to Aient" type = set(string) } data "aws_caller_identity" "current" {} data "aws_region" "current" {} resource "aws_s3_bucket" "aient_firehose_backup" { bucket = "aient-firehose-backup-${data.aws_caller_identity.current.account_id}-${data.aws_region.current.name}" } data "aws_iam_policy_document" "firehose_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["firehose.amazonaws.com"] } } } resource "aws_iam_role" "firehose" { name = "aient-firehose-http-delivery" assume_role_policy = data.aws_iam_policy_document.firehose_assume_role.json } data "aws_iam_policy_document" "firehose_s3_backup" { statement { actions = [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:ListBucket", "s3:ListBucketMultipartUploads", ] resources = [aws_s3_bucket.aient_firehose_backup.arn] } statement { actions = ["s3:PutObject"] resources = ["${aws_s3_bucket.aient_firehose_backup.arn}/*"] } } resource "aws_iam_role_policy" "firehose_s3_backup" { name = "aient-firehose-s3-backup" role = aws_iam_role.firehose.id policy = data.aws_iam_policy_document.firehose_s3_backup.json } resource "aws_kinesis_firehose_delivery_stream" "aient_cloudwatch" { name = "aient-cloudwatch-logs" destination = "http_endpoint" http_endpoint_configuration { url = var.aient_firehose_endpoint_url name = "Aient CloudWatch Logs" access_key = var.aient_firehose_access_key role_arn = aws_iam_role.firehose.arn retry_duration = 300 buffering_size = 1 buffering_interval = 60 s3_backup_mode = "FailedDataOnly" request_configuration { content_encoding = "NONE" } s3_configuration { role_arn = aws_iam_role.firehose.arn bucket_arn = aws_s3_bucket.aient_firehose_backup.arn buffering_size = 10 buffering_interval = 300 compression_format = "GZIP" } } depends_on = [aws_iam_role_policy.firehose_s3_backup] } data "aws_iam_policy_document" "cloudwatch_logs_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["logs.amazonaws.com"] } condition { test = "StringLike" variable = "aws:SourceArn" values = [ "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*", ] } } } resource "aws_iam_role" "cloudwatch_logs_to_firehose" { name = "aient-cloudwatch-logs-to-firehose" assume_role_policy = data.aws_iam_policy_document.cloudwatch_logs_assume_role.json } data "aws_iam_policy_document" "cloudwatch_logs_to_firehose" { statement { actions = [ "firehose:PutRecord", "firehose:PutRecordBatch", ] resources = [aws_kinesis_firehose_delivery_stream.aient_cloudwatch.arn] } } resource "aws_iam_role_policy" "cloudwatch_logs_to_firehose" { name = "aient-cloudwatch-logs-to-firehose" role = aws_iam_role.cloudwatch_logs_to_firehose.id policy = data.aws_iam_policy_document.cloudwatch_logs_to_firehose.json } resource "aws_cloudwatch_log_subscription_filter" "aient" { for_each = var.cloudwatch_log_group_names name = "aient" log_group_name = each.value filter_pattern = "" destination_arn = aws_kinesis_firehose_delivery_stream.aient_cloudwatch.arn role_arn = aws_iam_role.cloudwatch_logs_to_firehose.arn emit_system_fields = ["@aws.account", "@aws.region"] depends_on = [aws_iam_role_policy.cloudwatch_logs_to_firehose] } ``` ## Verify the connection After applying the AWS configuration: 1. Write a test log line to a subscribed log group. 2. Wait for the Firehose buffer interval, usually about 60 seconds with the Terraform example. 3. In Aient, the drain should move from **No data yet** to **Receiving**. 4. In Firehose metrics, check delivery success and HTTP response codes. 5. In the backup S3 bucket, failed objects indicate Firehose exhausted retries. Useful smoke-test messages: ```txt theme={"theme":{"light":"github-light","dark":"github-dark"}} INFO Aient Firehose smoke test ERROR Aient Firehose smoke test error ``` Structured error logs work best for problem creation: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "level": "ERROR", "message": "Aient Firehose smoke test error", "error": "SmokeTestError", "stack": "SmokeTestError: test\n at smoke (index.js:1:1)" } ``` ## Troubleshooting | Symptom | Likely cause | Fix | | - | - | - | | Aient shows **Firehose auth** | Firehose access key does not match the copy-once Aient value | Rotate the Aient drain and update the Firehose access key | | Aient shows **Firehose payload** | Firehose is not sending the standard CloudWatch Logs payload, request content encoding is not `NONE`, or records exceed limits | Disable Firehose request gzip, remove transformation processors, and lower Firehose buffer size | | Firehose reports HTTP 413 | The batch is too large | Set `buffering_size = 1` and reduce log volume with a narrower filter pattern | | Firehose retries then writes to S3 backup | Aient returned a retryable error or could not forward telemetry downstream | Keep S3 backup enabled and share the Firehose request ID with Aient support | | Logs arrive but no problems are created | Logs are informational or do not contain exception/severity fields | Send structured error logs with `level`, `message`, `error`, and `stack` fields | ## Related * [OpenTelemetry endpoints](/reference/otlp-endpoints) - send standard OTLP traces and logs to Aient. * [Server telemetry quickstart](/quickstart/server) - add the Aient SDK for richer spans and release metadata. * [Upload source maps](/source-maps/upload) - resolve bundled stack frames to original source. # Changelog Source: https://docs.aient.ai/changelog Release notes for Aient covering new features, SDK updates, webhook changes, integrations, and bug fixes shipped each week to the platform. **2026-10-26** Week of October 26 ## Updates **Follow server requests from logs to traces.** `@aient/otel` 2.2.2 includes correlated access logs for recorded inbound HTTP requests, including successful responses, with stable HTTP attributes and configurable query redaction. Duration metrics remain enabled when access logs are disabled, and referrer URLs receive credential redaction. Review log volume and application-specific sensitive query keys when upgrading. See [HTTP request telemetry](/reference/http-request-telemetry) for configuration and verification. **Keep workspace sync moving.** Aient CLI 0.11.31 retries a failed Git-cache sync through a direct transfer and waits up to 15 minutes while the sandbox prepares the workspace. Repeating a sync at the same commit skips unchanged work or replaces a changed remote workspace. See [sync recovery](/ai-tools/cli/run-and-sync#recover-an-interrupted-workspace-publication). **Keep installed pnpm dependencies across a sync.** Add `--preserve-node-modules` to `sandbox sync` or `sandbox exec --workspace` to retain existing dependency directories. Run your package installation command when dependency files change. See [dependency preservation](/ai-tools/cli/run-and-sync#keep-installed-pnpm-dependencies). **Clear obsolete workspace recovery files.** Retained CLI operations now expire after 14 days. Listing operations also removes those whose sandboxes are confirmed deleted; uncertain results keep the files. See [retention and cleanup](/ai-tools/cli/run-and-sync#retention-and-cleanup). **Give Aient context before planned work.** MCP operational notices describe maintenance, rollouts and incidents in one environment. Aient considers overlapping notices during triage and investigation. Posting a notice does not mute or close Problems. Connect with human OAuth and approve the write scope to post or end one. **2026-10-19** Week of October 19 ## Updates **Recognise incomplete command output.** Aient CLI 0.11.29 reports when a reconnect skips output that the server no longer retains. It keeps the retained tail separate from stdout and exits with status 74, even if the command succeeded. Inspect the original execution before deciding whether it is safe to rerun. See [output recovery](/ai-tools/cli/executions-and-files#incomplete-output-after-a-reconnect). **Resolve conflicting sandbox options.** Reusing a sandbox name with different size, CPU, memory or environment options now returns a clear conflict. Retry with the original options or choose a different name. See [sandbox-name recovery](/ai-tools/cli/troubleshooting#sandbox_allocation_terms_conflict). **Update external-failure queries for the SDK.** The failure helpers in `@aient/otel` 2.1.1 write `aient.external.*` attributes. Update queries that use `glimt.external.*`, keeping a fallback for older telemetry. The helpers attach the external system, operation and error to an existing span. See [external-service failures](/reference/external-failures). **Connect an MCP client without a browser.** The dedicated `https://aient.ai/mcp/signup` endpoint supports email verification for new and existing accounts. The returned token grants read and onboarding access; conversation writes still require separate OAuth consent. Ordinary OAuth clients continue to use `/mcp`. See [email signup and recovery](/ai-tools/mcp-signup). **2026-10-12** Week of October 12 ## Updates **Recover interrupted workspace transfers automatically.** Aient CLI 0.11.28 retries supported transient network failures and incomplete responses while keeping the files captured at the start of the operation. Numbered Git-cache uploads retry only the affected part. Retries are bounded; cancellation and authorisation failures still stop the transfer. Use your existing workspace commands with no new flag. See [transfer recovery](/ai-tools/cli/run-and-sync#recover-an-interrupted-workspace-publication). **Send selected files to an Agent's checkout.** Aient CLI 0.11.27 adds `agent worktree sync` for one-shot updates to an Agent's current primary checkout on a compatible runtime. Select up to 128 existing files, each at most 64 KiB. Confirmed writes replace files atomically, then ask the Agent to reread them. If that notification fails, retry the notification without repeating the writes. See [Agent checkout sync](/ai-tools/cli#sync-selected-files-into-an-agent-checkout) for compatibility and overwrite limits. **Watch source edits through supervised execution.** Aient CLI 0.11.26 adds the experimental `--live --live-transport exec` mode for compatible running sandboxes. Start the watcher before the application and select existing files of at most 64 KiB each. An unconfirmed write stops the watcher and requires reconciliation. See [live-sync setup and limits](/ai-tools/cli/run-and-sync#live-sync-through-supervised-execution). **Start an Aient conversation from your MCP assistant.** With a human OAuth connection and the approved write scope, call `send_thread_message` without a thread ID to start a conversation. Keep the returned ID for follow-ups and use `get_thread` to read the response. Organisation viewers cannot start conversations. See [MCP conversations](/ai-tools/mcp-conversations) for setup and uncertain-result recovery. **2026-10-05** Week of October 5 ## Updates **Watch selected source files in a running sandbox.** Aient CLI 0.11.23 adds experimental live sync: upload explicitly selected files, then send local saves through a shell stream or the files API. Each file is replaced atomically. Initial publication overwrites the selected remote files; concurrent edit protection, deletion propagation, and reconnect recovery are not supported. See [setup and limits](/ai-tools/cli/run-and-sync#experimental-selected-file-live-sync). **Reuse Git history when synchronising an existing sandbox.** Aient CLI 0.11.22 reuses the shared Git cache for eligible native V3 workspaces in `sandbox sync` and `sandbox exec --workspace`. Interrupted preparation retains the original captured files, so repeating the command can continue that transfer after further local edits. Recovery stays tied to the original allocation and workspace, and commands start only after activation succeeds. See [workspace recovery](/ai-tools/cli/run-and-sync#recover-an-interrupted-workspace-publication). **2026-09-28** Week of September 28 ## Updates **Aient CLI 0.11.15 through 0.11.18 make sandbox work faster to recover and easier to inspect safely.** Cold Git-cache lookups can continue from a completed local seed instead of waiting on a slow remote fetch. You can save one bounded, read-only file from an Agent-owned worktree, and a compatible supervised Docker daemon can remain available while a workspace synchronises. Retained workspace recovery preserves the original capture after cancellation or a stale base, rather than substituting later local edits. **Clearer sandbox capacity recovery.** Aient CLI 0.11.17 and 0.11.20 preserve the requested sandbox name when recovering from a confirmed capacity rejection. For an uncertain creation result, the CLI prints the allocation ID and exact recovery command so you can check it without creating another allocation. Commands use the logical sandbox name rather than physical runtime metadata. **Run a command in an Agent-owned sandbox without creating another one.** Aient CLI 0.11.18 authorises the exact existing workspace and sandbox before starting a command from `aient agent chat THREAD`. Reconnect, inspect, wait for, or cancel that same execution with its stable ID. **2026-09-21** Week of September 21 ## Updates No public product updates shipped this week. **2026-09-14** Week of September 14 ## Updates **Aient CLI 0.11.11 gives interrupted command output a safe recovery path.** If `agent execution status`, `wait`, or `cancel` cannot write its local receipt, rerun `aient agent execution status EXECUTION` with the preserved execution selector. Aient reauthorizes and observes the same execution; it does not start a replacement command or repeat a cancellation. Successful output still retires the local selector, and recovery continues to require the original server evidence and current authorization. **Environment-specific service commit evidence.** `aient services list` now reports observed commits and timestamps for each environment, including staging and QA, in both terminal and JSON output. These are telemetry observations, not deployment confirmation; older organisation-wide commit SHAs are omitted when their environment is unknown. **2026-09-07** Week of September 7 ## Updates **Aient CLI 0.11.10 faster eligible sandbox starts.** An exact-clean workspace at the default repository root can reuse one ready prewarmed repository member. Eligible starts send no committed Git bytes and skip sandbox creation and workspace materialisation. Other workspaces continue through the established cold or retained-operation paths, so we do not promise this path for every command or a fixed activation time. **More reliable CLI preparation and project access.** Immutable workspace capture can overlap remote preflight without displacing an exact retained operation. The CLI now ignores stale derived Git metadata that refers only to pruned unreachable objects, while continuing to reject a genuinely missing reachable object before network activity. Managed project access can also renew after a laptop wakes from sleep. **2026-08-31** Week of August 31 ## New features **Flutter debug artifacts, including Apple dSYMs.** `@aient/sourcemaps` 0.3.4 now validates and uploads matching Dart AOT symbols and thin Apple dSYM DWARF members from release CI. The upload is tied to the exact AAB or IPA, commit, release ID, and architecture, so Aient can retain the evidence needed to resolve supported mobile frames. Android R8 mappings and deferred loading-unit symbols remain retain-only while their upload support is not yet available. ## Updates **Aient CLI 0.11.6 through 0.11.9.** Sandbox commands now keep one execution identity through an in-process reconnect, and explicit reattachment can resume retained output from a cursor without launching a second command. The JSON reconnect identity is now clearly separate from server admission. Repeated workspace preparation resumes only the exact retained operation when source and authority still match. Repository-bound projects can use the organisation development-environment default, and cache-backed workspaces transfer only an adjacent Git-object delta when a verified ancestor is available. Timing receipts can now distinguish candidate durability from activation when the server provides that evidence. **2026-08-24** Week of August 24 ## Updates **Aient CLI 0.10.20 customer-agent messages.** We added `aient agent message --immediate` for cooperative delivery to an active task at a stable boundary. It never cancels or replaces that task. Reuse the stable operation ID when retrying an ambiguous result; the receipt reports whether Aient accepted, queued, started, or had already recorded the message. **Aient CLI 0.10.17 and 0.10.18 safer long-running sandbox work.** Use `--detach --keep` when a sandbox command must outlive the initiating client, then inspect, attach to, wait for, or cancel the same execution by its stable ID. The CLI can also list, classify, and dry-run explicit runtime-service-to-repository mappings before applying them. **Aient CLI 0.10.19 more resilient macOS project access.** Project access now survives a normal APFS remount while Aient continues to reject replaced directories. If an older definition needs repair, use `aient --profile NAME auth export --store-in-project` after confirming the host profile is healthy. **2026-08-17** Week of August 17 ## Updates **Aient CLI 0.10.16 project access.** On macOS, `aient auth login` now sets up bounded project access by default for a configured checkout. Aient keeps the refresh authority in Keychain and renews the project-local, short-lived access envelope in the background, so a verified checkout can continue working after the login terminal closes. `auth status`, `auth logout`, and `auth export --store-in-project` now make that lifecycle explicit. **Selected nested repositories for sandbox work.** Sandbox runs and synchronisation can now transfer only the submodules a command needs. Use `--submodules=selected` with `--submodule PATH` to include an exact clean child repository, or `--submodules=gitlinks` when child contents are unnecessary. Aient rejects implicit or ambiguous submodule transfer before upload. **2026-08-10** Week of August 10 ## Updates **Customer MCP connections.** Organisation owners and administrators can now attach a trusted remote MCP server to one Aient environment, review its discovered tools, approve a bounded subset, and make it available to new Sparkles tasks. Connections keep credentials write-only and fail closed when their approval or configuration changes. **Aient CLI 0.10.7.** The CLI now gives remote workspace synchronisation clearer path-ownership controls. You can preserve or reset named generated directories without changing local files, while incompatible server capabilities or competing workspace layouts fail safely before upload. **OpenTelemetry quickstarts for more runtimes.** New setup guides cover standard OTLP/HTTP instrumentation for Python, Java, .NET, Ruby, PHP, Go, and Elixir, including the deployment metadata Aient uses to correlate traces with a release. **2026-08-03** Week of August 3 ## Updates **Aient CLI 0.9.1 for invited preview.** The CLI now uses negotiated atomic activation when it synchronises a workspace to a retained sandbox. If another workspace layout owns the target, it returns actionable contention guidance instead of dispatching or replaying against ambiguous state. **Browser SDK click and identity lifecycle.** The Browser SDK can record one point span for each eligible user click. Apps can now identify, clear, and read their current correlation identity, while server-authenticated identity remains authoritative. **Published posts are easier to share.** Aient blog posts now include copy-link, email, and X share actions, along with responsive author profiles. **2026-07-27** Week of July 27 ## Updates **Aient CLI 0.8.2 for invited preview.** Retained sandbox `status`, `wait`, and `logs` now use the environment that owns the sandbox. Owners and administrators can also set encrypted environment secrets from the CLI; values are write-only and never returned. Workspace synchronisation now treats `--timeout` as an inactivity limit while bytes are moving, so healthy large uploads can take longer than the default five minutes. A retry starts a fresh delta or snapshot. **Safer customer sandbox control.** Teams can choose a product-governed `small`, `medium`, or `large` compute class for an environment-backed sandbox. Workspace synchronisation can now start from all eligible non-Git files and explicitly exclude paths such as build output and `.env` files. **2026-07-20** Week of July 20 ## Updates **Reopen resolved problems through MCP.** Assistants with MCP write scope can now reopen a resolved problem when new production evidence changes the conclusion, while preserving its existing evidence and remediation history. **Clearer metered-usage billing.** Billing Settings now distinguishes recorded usage for the current period, estimated usage that has not yet reached an invoice, and provider invoices and payments. Interim invoices can be issued when outstanding usage reaches the subscription threshold. **2026-07-13** Week of July 13 ## Updates **GitHub App connection checks.** Aient now verifies the installer and ties each GitHub App installation to the selected organisation before linking it. If an installation is already linked elsewhere, Aient explains the next step to resolve it. **MCP sign-in continuation.** MCP OAuth onboarding now carries a genuine sign-in or MFA flow back to the authorisation step while keeping ordinary visitors behind the launch gate. ## Fixes * The browser SDK now ignores Firefox Reader Mode probe errors, keeping non-application noise out of your telemetry. **2026-07-06** Week of July 6 ## New features **Remediation evidence packet.** Problem case files, MCP `get_problem`, and remediation prompts now share a current remediation snapshot with PR bindings, verification evidence, prior handoff context, and exact artifact handles. Agents and humans start from the same case evidence instead of re-reading raw thread history. **Public blog and feature pages.** Aient now has a public blog with static MDX, Mermaid diagrams, feeds, and SEO metadata, plus dedicated feature detail pages with product screenshots from the homepage. **MCP setup in settings.** Teams can revisit MCP connection setup from settings, with shared configuration guidance across onboarding and signed-in setup surfaces. ## Updates **Problem view readability.** Problem pages now lead with the case document and keep raw inspectors collapsed by default, while retaining raw details for debugging. **Remediation review and verification loop.** Aient now tracks PR status, approval comments, delivery waits, deploy evidence, verification confidence, and human questions more consistently inside the active remediation attempt. **Human answer continuation.** Answers to remediation questions now wake the same root agent task and stay in the same conversation lane, reducing lost context after a human decision. ## Fixes * Public MCP and x402 thread tools now fail closed before reading private agent-designer replay threads or their artifacts. * Source-map uploads and Restate ingress recover more reliably from transient network failures. * Telemetry ingest now applies smaller ClickHouse batches to reduce pressure during high-volume periods. * Resolved problems and terminal remediation PR feedback are less likely to reopen stale work or show old closure states. * Agent remediation edits and workspace paths now preserve safer path boundaries and avoid exposing backing worktree details. **2026-06-29** Week of June 29 ## New features **MCP AWS Firehose drain management.** Authorized MCP clients can now list, create, rotate, and revoke AWS Firehose drains for an environment, giving agentic setup flows the same lifecycle controls as the dashboard. **Source-map deployment tracking.** Source-map uploads now record deployment SHAs and compress artifacts on upload, giving stack trace resolution a more durable link between running code and retained source maps. **Remediation run history.** Agent remediation runs now persist runtime events into thread records, so problem threads keep clearer evidence about what the agent did, where it got blocked, and how it recovered. ## Updates **Remediation PR feedback loop.** Aient now binds GitHub bot reviews, inline review comments, PR status snapshots, branch evidence, and review-triggered waits into remediation threads more consistently, so agents can keep fixing PRs without losing current context. **Remediation sandbox reliability.** Sandbox lifecycle checks now expose contract mismatches and readiness failures as explicit telemetry, improving recovery when a workspace resumes, hibernates, or reports an unexpected state. **Source-map retention.** Retention now keys off deployment SHAs, service configuration, canonical commit SHAs, and safer object-delete paths, reducing accidental artifact loss and keeping symbolication evidence available longer. ## Fixes * Early-access request submissions now throttle by requester identity, reducing duplicate requests without blocking unrelated teams. * Billing webhooks now replay duplicate Stripe subscription and invoice events safely. * MCP setup now tolerates placeholder organisation selectors while preserving authenticated tenant scope. * Remediation agents recover more reliably from provider finish errors, content-filter interruptions, sandbox loss, and compacted context. * Remediation PR delivery now preserves assigned branches, pushed head SHAs, active obligations, and already-advanced completion states more accurately. * Source-map retention no longer times out on large keep-set queries or fails on MinIO delete checksum requirements. **2026-06-22** Week of June 22 ## New features **Public security page.** Aient now has a public security and trust page with responsible disclosure details, contact paths, and a published `security.txt`, giving teams a clearer place to review Aient's security posture before rollout. **Teams inbound replies.** Microsoft Teams messages now flow through the same channel-turn ingestion path as other collaboration surfaces, so Teams replies can carry problem and remediation context back into Aient more reliably. **Request access form.** Public Aient pages now route early-access teams to a first-party request flow, keeping rollout requests on-site and making the next step clearer for teams evaluating the platform. ## Updates **Remediation status reliability.** Remediation threads now preserve the active source of truth when older blockers, review facts, or workspace epochs arrive late, keeping problem status, PR context, and verification history aligned. **PR review context.** Agent-authored remediation work now hydrates more source context from GitHub comments and review events before applying corrections, improving follow-up quality on requested changes. **Telemetry ingest health.** Vercel Drain and AWS Firehose ingest paths now report receiver health more consistently, making telemetry setup and troubleshooting easier to verify. ## Fixes * Onboarding no longer shows a stale URL state after returning from OAuth. * Service selection now stays in a clear loading state during long-running discovery instead of presenting a premature error. * Slack workspace selection copy is clearer during onboarding. * Guarded GitHub PR review failures are now surfaced in remediation threads instead of failing silently. * Thread workspaces remain visible across remediation epoch changes. **2026-06-15** Week of June 15 ## New features **Vercel Drain ingestion.** Teams can now connect Vercel custom-endpoint Drains for logs and traces from **Settings → Environments → Vercel Drains**. Aient verifies Vercel signatures, stamps telemetry to the selected organisation and environment, and sends it through the same problem detection and remediation pipeline as SDK ingest. **Vercel integration guide.** The docs now include a Vercel Drain setup guide with the Aient settings flow, Vercel destination steps, signature verification guidance, and what to expect once telemetry starts arriving. **AWS CloudWatch Logs ingestion.** Teams can now connect CloudWatch log groups to Aient through Amazon Data Firehose from **Settings -> Environments -> Drains**. Aient accepts the standard CloudWatch Logs payload, verifies the copy-once Firehose access key, and turns error-class AWS logs into the same problem detection and remediation flow as other telemetry. **AWS Firehose integration guide.** The docs now include an AWS setup guide with Firehose destination settings, CloudWatch Logs subscription filters, IAM role guidance, Terraform examples, verification steps, and common troubleshooting paths. **Remediation PR review feedback.** GitHub review feedback on agent-authored remediation pull requests now routes back into the remediation thread, including requested-changes and dismissed-review states, so agents can react to review context without losing the PR binding. ## Updates **Server-side source maps for agents.** Aient's instrumentation guidance now treats `@aient/sourcemaps` uploads as required for transpiled or bundled Node, Bun, and Deno services, giving server-side stack traces the same symbolication path as browser and Next.js bundles. **Vercel observability page.** Aient now has a public Vercel integration page that explains the Drain-based path from Vercel logs and traces to problem detection, root cause analysis, and reviewed fix pull requests. **Problem lifecycle reliability.** Problem transitions and projections now preserve monotonic lifecycle state when delayed or replayed events arrive, reducing stale status regressions in problem details, remediation threads, and downstream exports. ## Fixes * Vercel Drain management now uses tighter admin controls, safer rotation and revoke behavior, and retry-aware handling for upstream ingest failures. * GitHub identity-link notices in remediation threads now name the GitHub actor that needs linking. * Source-map bulk upload signing now retries transient signing failures before blocking upload batches. * Remediation PR status projections now keep verified and merged PR states visible on problem details and case files. **2026-06-08** Week of June 8 ## New features **Public agent setup page.** Aient now has a public `/agents` entry point for teams and agents that need setup paths, documentation links, and agent-commerce context before signing in. **Pricing and comparison pages.** Aient now has public `/pricing` and `/compare` pages that explain the base-plus-usage model, budget controls, and how Aient differs from other AI SRE and autonomous remediation tools. **Retry failed remediations.** Failed remediation attempts now expose structured failure evidence and retry actions from the dashboard and MCP tools, so teams can restart eligible work without losing the original problem context. **Problem issue-kind labels.** Problem lists, details, routing context, and agent exports now show whether a problem looks like application code, infrastructure, database, or another issue type, helping teams send work to the right owner faster. **MCP onboarding activation tools.** Agents connected through MCP can now read activation status, submit supported onboarding steps, and retry repository discovery when the token has the right scope. **MCP service-to-repository linking.** Authorized MCP clients can now connect observed telemetry services to the repository and path Aient should use for source maps and remediation context. **Telemetry-aware billing setup.** Aient now prompts billing setup after onboarding and first telemetry, keeping payment setup out of the way until the platform has real signal to protect. **Teams as a collaboration channel.** Teams can now choose Microsoft Teams during onboarding, route problem notifications to Teams destinations, and manage collaboration delivery with the same readiness checks used for Slack. **Official MCP Registry listing.** Aient's hosted MCP server is now listed as `ai.aient/mcp` in the official MCP Registry, giving coding agents a standard discovery path for Aient context and remediation tools. **Confirmed problem resolution.** Dashboard owners can now confirm a pending agent-proposed resolution from the problem action flow, keeping closure explicit while preserving the agent's evidence trail. ## Updates **Source-map evidence in problem cases.** Problem case files now include resolved source-map stack details and call-chain evidence, giving coding agents better context before they prepare a fix. **Richer problem link previews.** Shared problem links now unfurl with the real problem title in supported clients, making incident context easier to recognize before opening Aient. **Remediation workspace reliability.** Aient now handles workspace materialization failures as explicit states and authenticates GitHub operations more consistently when preparing remediation pull requests. **Problem list scalability.** Large problem, label, dashboard, service-health, and scanner reads now split oversized filters into bounded requests, so high-volume organisations keep loading reliably as their history grows. **Remediation PR readiness.** Aient now evaluates GitHub commit status contexts alongside check runs before treating an agent-authored remediation PR as ready for review. **GitHub App key compatibility.** Aient now accepts the PKCS#1 private keys GitHub Apps provide and normalizes them for JWT signing, reducing setup failures for GitHub-backed remediation. **MCP setup scopes.** MCP setup now shows read and write scope controls, with generated configuration that requests write access when remediation control is needed. **Browser error instrumentation hardening.** The browser SDK now tolerates null or missing runtime error messages while filtering hydration mismatch noise. ## Fixes * Remediation attempts can now record already-fixed, no-fix, and awaiting-human outcomes without turning them into false "no PR created" failures. * Problem routing and verification now avoid more stale retry loops, reducing stuck or misleading remediation states. * Problem action controls now have more consistent parity across problem surfaces. * Agent execution budgets now account for tool time more accurately, reducing false retry exhaustion. * GitHub authentication is now applied more consistently when remediation agents publish changes. * Telemetry verification and anomaly debugging now derive tenant scope from authenticated access instead of caller-supplied identifiers. * Agentic GitHub onboarding now verifies installer control before linking an installation to an onboarding session. * Teams destination deletion now stays scoped to Teams routes. * The Problems page no longer records URL-state hook errors during initial server rendering. * Remediation agents now retry transient model stream failures while keeping quota and authentication failures terminal. **2026-06-01** Week of June 1 ## New features **Problem Details redesign and agent export.** Problem pages now use stable `PRB-n` references, clearer evidence sections, and bulk export actions so teams can move problem context into coding agents without losing the trail. **Remediation safety controls.** Automatic remediation now follows severity policy gates, giving teams a clearer boundary between investigation, draft work, and changes that still need review. ## Updates **GitHub and agent onboarding reliability.** Aient now carries GitHub installation and account-binding context more reliably through agentic setup flows, including x402-backed onboarding paths. **Problem routing and collaboration delivery.** GitHub webhook ingestion, Slack delivery, and threaded updates now recover more consistently from duplicate events, stale bindings, and temporary provider failures. **Telemetry ingest hardening.** Public telemetry pipelines now guard oversized payloads and route collector traffic more reliably, improving trace and log availability during incident investigation. ## Fixes * Problem disposition actions now return honest pending states instead of false timeout failures. * Investigated problems no longer show as actively being worked when the remediation state has moved on. * Source map verification now retries through short storage delays and replaces stale artifact hashes. * Remediation workspaces now authenticate Git and GitHub operations more reliably before preparing pull requests. **2026-05-25** Week of May 25 ## New features **Bulk source map uploads.** Source map upload APIs now batch larger sets safely, use bounded chunk sizes, and recover from invalid upload paths before they block stack trace resolution. **Problem case documents.** Problem detail pages now show a structured case document that brings evidence and remediation context into one reviewable surface. **Stale problem auto-resolution.** Aient can now close problems that stop reproducing after enough quiet time, so teams can distinguish natural recovery from manual fixes. ## Updates **GitHub connection recovery.** GitHub connections recover more reliably when installation details change, keeping repository suggestions and PR permissions visible. **Telemetry explorer polish.** The logs view now handles malformed timestamps, reduces low-value metric noise, and keeps Problems above Services so investigation starts from the highest-value surface. **Telemetry ingest reliability.** Aient more reliably receives traces, logs, and metrics from monitored systems. ## Fixes * Problem detection now continues correctly after quiet scan windows. * Affected user and service counts now stay aligned with the current problem state. * Kubernetes problems now coalesce by workload owner instead of unstable pod identity. * Source map commit verification now retries through short upload delays and replaces stale artifact references before they block stack trace resolution. **2026-05-18** Week of May 18 ## Updates **Problem delivery is more resilient.** Missing Slack installs, stale channel permissions, and temporary telemetry query failures now degrade explicitly instead of interrupting problem delivery. **Problem routing is more consistent.** Follow-up work tightened the transition between detection and routing so users see fewer stuck or duplicated problem states. ## Fixes * Repository discovery now preserves Git credentials during fetches, reducing failed repository setup attempts. * Slack follow-up threads now reset correctly after a constrained reply. **2026-05-11** Week of May 11 ## New features **Environment selector management.** Teams can manage environments from the selector, making it easier to switch between telemetry scopes without losing context. **User account MFA management.** Account settings now include user-managed MFA controls, strengthening account security without requiring support intervention. ## Updates **Telemetry and operations dashboards improved.** Dashboard overview latency was reduced, telemetry trace empty states were fixed, and operators have a clearer view of system health. **Source map resolution is more accurate.** Server bundle uploads, Turbopack aliases, and chained package maps now resolve more reliably, improving the stack traces Aient uses during triage. ## Fixes * Slack account-link recovery and PR thread replies were hardened. * Web chat replies no longer stall after stale background work. * Malformed authentication payloads are redacted from logs. **2026-05-06** Week of May 4 ## Updates **Copy your organisation ID from settings.** The organisation settings page now shows your organisation ID in a dedicated panel with a one-click copy button. Use it when filing a support request or correlating webhook payloads, where the same UUID appears as `organisationId` and in the CloudEvent `source` URL. **2026-05-04** Week of May 4 ## New features **Public event registry at `registry.aient.ai`.** CloudEvent `dataschema` URLs and event aliases now resolve from a dedicated public registry, independent of the app, with stable cache headers and immutable schema revisions. See the [event registry reference](/reference/event-registry). **Aient end-to-end PR checks expose cache and timing evidence.** PR E2E runs now show cache evidence, build cache flags, and runner startup timing in the check summary so you can see what was reused between runs. **Standalone integration docs.** Aient docs now run as a standalone Mintlify site focused on telemetry SDKs, source maps, OTLP ingest, and webhook event schemas. ## Updates **Slack thread routing improvements.** Aient now reuses existing PR thread bindings for incoming Slack and GitHub PR events, reducing duplicate threads and dropped messages. ## Fixes * PR E2E checks now finish cleanly when the runner disappears, instead of stalling. * PR E2E check summaries now show an explicit "preparing the test runner" status during startup, so you see meaningful progress before the runner reports its first phase. * Stale PR threads are no longer recreated when a PR comment arrives after the original thread bootstrap. * Provider error qualifiers and details are preserved end-to-end in OpenTelemetry traces, making LLM provider errors easier to debug. * Populated bars now render correctly on the problem histogram chart and sparklines after timestamp bucket alignment. # Aient integration docs Source: https://docs.aient.ai/index Connect apps to Aient with OpenTelemetry SDKs, source map uploads, OTLP ingest endpoints, and CloudEvents webhooks for problem lifecycle events. # Aient integration docs Use these docs when connecting applications to Aient. They cover server and browser telemetry, source map uploads, webhook delivery, and the generated webhook event schema reference. ## Start here * [Instrument a server app](./quickstart/server) with `@aient/otel`. * [Instrument a browser app](./quickstart/browser) with `@aient/otel-browser`. * [Connect a Vercel Drain](./vercel/drain) to ingest Vercel logs and traces with near-zero instrumentation. * [Connect AWS CloudWatch Logs](./aws/firehose) through Amazon Data Firehose without changing application code. * [Upload source maps](./source-maps/upload) with `@aient/sourcemaps` so minified frames resolve to original source. * [Subscribe to webhooks](./webhooks/overview) for Aient problem lifecycle events. * [Read common questions](./questions) about what Aient does and how teams use it. ## Public ingest Aient accepts OTLP HTTP traces and logs at the public endpoints documented in [OpenTelemetry endpoints](./reference/otlp-endpoints). Use publishable environment keys in application code. # Common questions about Aient Source: https://docs.aient.ai/questions Answers to common questions about what Aient does, how it ingests OpenTelemetry signals, groups problems, integrates Slack and GitHub, and drives remediation. # Questions These are the questions engineering leaders, operators, and agents usually ask before connecting Aient. The short version: Aient sits on top of your telemetry, turns noisy signals into problems, and helps drive each problem toward a reviewed fix. ## 1. What is Aient? Aient is an AI operations layer for software teams. It ingests telemetry, detects when something goes wrong, and groups related signals into a problem. From there, it explains what changed and drives remediation through Slack, GitHub, webhooks, and agent tools. ## 2. How can Aient know what goes wrong? Aient learns from the signals your applications emit: traces, logs, exceptions, browser errors, release metadata, and source maps. OpenTelemetry provides the runtime context. Source maps and commit metadata connect minified browser frames back to source. Integrations such as GitHub and Slack add the collaboration context Aient needs to route and explain each issue. ## 3. What signals does Aient ingest? The public integration surface accepts OTLP HTTP traces and logs. The Aient SDKs configure the endpoints, auth headers, service names, and release metadata for Node.js and browser apps. Webhook events and the Slack, GitHub, and Linear integrations add lifecycle and collaboration context around each problem. ## 4. How do I add Aient to my services? Install `@aient/otel` in server services and `@aient/otel-browser` in browser apps. Set an Aient publishable environment key, and pass a stable `serviceName` plus release metadata. For browser apps, upload source maps from CI with `@aient/sourcemaps` using the same service name and commit SHA. ## 5. What happens after telemetry reaches Aient? Aient groups related telemetry into a problem, enriches it with service and release context, triages likely impact, and routes the next action. Depending on your configuration, that action can be a Slack notification, a webhook, an investigation, a GitHub issue, or a remediation PR. ## 6. What is a problem? A problem is Aient's durable record for one customer-impacting issue. It is not a single log line or one trace. It is the grouped lifecycle around a failure pattern: detection, triage, routing, remediation, verification, closure, and possible regression. ## 7. How does Aient avoid noisy duplicate alerts? Aient treats repeated signals as evidence for the same problem when they share the same failure pattern and context. The product model is problem-first rather than event-first, so users see one evolving issue instead of a stream of repeated alerts. Routing policies and cooldowns control when Aient notifies again. ## 8. Can Aient fix problems automatically? Aient can investigate and prepare remediation work, including GitHub pull requests, when the integrations and policies allow it. Treat it as a draft author, not an auto-merge system. Humans keep review and merge authority unless you build a stronger approval workflow around it. ## 9. Will Aient change production by itself? No. Aient can open PRs, create issues, send notifications, and call configured workflows, but production changes still depend on your review, CI, deployment, and approval process. ## 10. What do I see in Slack? Slack is a collaboration surface for problem updates. Aient can post concise problem milestones, keep discussion threaded, and accept user replies or mentions where the Slack integration is configured. The canonical problem state remains in Aient. ## 11. What does GitHub add? GitHub lets Aient connect failures to repositories, commits, source files, issues, and pull requests. That context is what turns "there was a spike" into "this release introduced this failure and here is a candidate fix." ## 12. Where do source maps fit? Source maps make browser crashes actionable. Without them, production stack frames often point to minified bundles. Uploading source maps with the matching service name and commit lets Aient resolve frames back to original files and lines. ## 13. What keys do I need? Use publishable environment keys in application telemetry code. Use server-side API keys only in trusted automation such as CI source-map uploads. Never ship a server API key to a browser or mobile app. ## 14. What data should I avoid sending? Do not attach secrets, auth tokens, raw request bodies, or sensitive user-entered content to spans or logs. Keep telemetry useful but minimal: service names, release metadata, error context, timings, and safe attributes. ## 15. How do webhooks work? Aient webhooks deliver tenant-selectable lifecycle events as CloudEvents structured JSON. Verify signatures, process events idempotently with the CloudEvents (`source`, `id`) tuple, and return success only after durable acceptance. ## 16. What do I use the MCP for? Use the Aient MCP when an AI assistant or coding agent needs live Aient context inside tools such as Cursor or Claude Code. It can read telemetry and problem context, and with onboarding/write scope it can report activation status, submit supported onboarding steps, and retry repository discovery. Keep payment, integration approval, and production-change authority on the normal consent and review surfaces. ## 17. How does Aient work with monorepos and many services? Give each runtime a stable `serviceName`, set release metadata on every deployment, and upload source maps with the same service and commit. Aient then has enough context to group failures by service and connect browser stacks to the right build. ## 18. How fast should I expect Aient to react? Aient is designed for operational loops measured in minutes, not quarterly reports. Exact timing depends on telemetry arrival, grouping, triage, routing, integrations, and the approval path for remediation. ## 19. How should I start safely? Start with one service and one environment. Send telemetry, upload source maps if it is a browser app, verify that Aient sees the service, then add Slack/webhooks/GitHub routing once the signal is useful. Expand after the first real problem lifecycle is clear. ## 20. Can an agent buy and configure Aient for me? Yes, provided you choose the right setup path. An autonomous wallet agent can discover Aient, pay with x402, create a wallet-backed organisation, and request remediation directly — see [Agentic x402 onboarding](./agentic-x402). An AI assistant can help a human team prepare setup safely — see [Agent-assisted setup](./agent-commerce) for what an assistant can and cannot do. ## 21. How does metered usage appear in Billing Settings? Billing Settings separates usage recorded for the current period, estimated usage that has not reached an invoice, and provider invoices and payments. Stripe can issue an interim invoice when outstanding metered usage reaches your subscription's threshold; remaining metered usage is billed at the end of the cycle. A remediation is not charged as an individual purchase. # Browser telemetry quickstart Source: https://docs.aient.ai/quickstart/browser Install @aient/otel-browser, register it at app boot with a publishable key, and send frontend traces and logs from React or Next.js apps to Aient. # Browser telemetry quickstart Install the browser package and start telemetry once during application boot. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} pnpm add @aient/otel-browser ``` ```ts theme={"theme":{"light":"github-light","dark":"github-dark"}} import { registerOTelBrowser } from '@aient/otel-browser' registerOTelBrowser({ publishableKey: import.meta.env.VITE_AIENT_PUBLISHABLE_KEY, serviceName: 'web', installationId: '0198d2f4-1111-7111-8111-111111111111', // caller-owned contextAttributes: { 'business.id': 'business_123' }, release: { commit: import.meta.env.VITE_COMMIT_SHA, branch: import.meta.env.VITE_COMMIT_REF, environment: import.meta.env.MODE, }, }) ``` ## React and Next.js Browsers only: call `registerOTelBrowser` from a **client** component after mount (not during render). Initialize **once** at a long-lived application boundary, then update user and business/workspace context without re-creating the SDK. Cleanup synchronously detaches instrumentation before asynchronously draining providers, so an immediate Strict Mode remount can register cleanly. The package must be the sole owner of the page's OpenTelemetry trace, context, propagation, and logs globals; registration rejects a competing owner without replacing it. ```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}} 'use client' import { useEffect, useRef } from 'react' import { registerOTelBrowser, type BrowserSDK } from '@aient/otel-browser' export function TelemetryInit({ analyticsConsent, userId, pseudoId, businessId }: { analyticsConsent: boolean; userId?: string | null; pseudoId?: string | null; businessId?: string | null }) { const sdkRef = useRef(null) useEffect(() => { const installationKey = 'my-app.otel.installation-id' if (!analyticsConsent) { localStorage.removeItem(installationKey) return } const installationId = localStorage.getItem(installationKey) ?? crypto.randomUUID() localStorage.setItem(installationKey, installationId) const sdk = registerOTelBrowser({ publishableKey: process.env.NEXT_PUBLIC_AIENT_PUBLISHABLE_KEY!, serviceName: process.env.NEXT_PUBLIC_OTEL_SERVICE_NAME ?? 'web', installationId, release: { commit: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA, branch: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF ?? 'local', environment: process.env.NEXT_PUBLIC_VERCEL_ENV ?? 'development', }, }) sdkRef.current = sdk return () => { sdkRef.current = null void sdk.shutdown() } }, [analyticsConsent]) useEffect(() => { if (userId || pseudoId) { sdkRef.current?.identify({ ...(userId ? { userId } : {}), ...(pseudoId ? { pseudoId } : {}) }) } else { sdkRef.current?.clearUser() } }, [analyticsConsent, userId, pseudoId]) useEffect(() => { sdkRef.current?.setContextAttributes(businessId ? { 'business.id': businessId } : null) }, [analyticsConsent, businessId]) return null } ``` In the App Router, render `TelemetryInit` from `app/layout.tsx` (or a client shell). Pass analytics permission from your consent manager as `analyticsConsent`, the authenticated id as `userId`, and only pass `pseudoId` when you have a distinct pseudonymous person identifier. The caller-owned installation ID is a separate application-installation lifetime and is persisted only while analytics consent permits it. For the Pages Router, mount the same component under `_app`. Use `NEXT_PUBLIC_*` only for values that are safe to expose to the browser (the publishable key is designed for that). ## Dynamic context Use static `attributes` for resource facts that do not change during the SDK lifetime, such as deployment metadata. Supply a caller-owned `installationId` for one application installation; the SDK emits it as `app.installation.id` but does not persist it. Use `contextAttributes` and `sdk.setContextAttributes()` for mutable correlation values such as the active business, account, or workspace. `identify()` replaces the whole user snapshot and `clearUser()` removes it while preserving the installation. Each span captures dynamic context when it starts. Updating context affects future spans and outgoing Fetch/XHR baggage, but does not mutate in-flight or ended spans. ## Click capture The SDK owns one document-level capture listener and emits exactly one synchronous point span for each eligible DOM click event that reaches the document, including synthetic clicks. A target handler may call `preventDefault()`, return `false`, or stop propagation without suppressing the span because capture has already run. Disabled targets are excluded. Click spans are point observations, not parents for application work triggered by the click. Fetch/XHR and other later spans keep their natural active-context parentage. ## Release metadata Use the same `serviceName` and `release.commit` when uploading source maps. That lets Aient connect browser stack frames to the correct original source. Use the full 40-character SHA; see [release metadata](../reference/sdk-browser#release-metadata) for passing your build commit explicitly. ## Privacy Do not attach secrets, auth tokens, raw request bodies, or user-entered sensitive content to spans or logs. Publishable keys identify only the Aient environment and are safe for browser code. Outgoing W3C baggage may carry `app.installation.id`, `enduser.id`, and `enduser.pseudo.id` as explicitly untrusted correlation claims. Email and role stay local. Aient backends still derive authenticated identity from the verified server session and overwrite the browser claim. See the [SDK reference](../reference/sdk-browser) for details. Dynamic context and browser baggage are untrusted correlation hints. Never use them to authorize access or replace server-side session and identity checks. Generic context keys under `enduser.*` are reserved and omitted. ## Transport `@aient/otel-browser` sends OTLP HTTP JSON payloads. `http/protobuf` is not bundled; legacy protobuf config falls back to JSON. # OpenTelemetry quickstarts Source: https://docs.aient.ai/quickstart/opentelemetry Instrument Python, Java, .NET, Ruby, PHP, Go, and Elixir applications with Aient OTLP ingest, bearer authentication, and deploy metadata. # OpenTelemetry quickstarts Use this guide when your service does not run on Node.js. Aient accepts standard OTLP/HTTP, so you can use the OpenTelemetry SDK or auto-instrumentation for your runtime. For Node.js, follow the [server quickstart](./server), then see [recording external-service failures](../reference/external-failures) for failure helpers and attribute queries. Every example sends traces to Aient with a bearer-authenticated publishable key and the release metadata Aient uses to group a deployment and correlate source maps. ## Set shared deployment values Create an Aient environment publishable key, then set these values in your deployment environment. Do not commit the key or a real token to source control. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} export AIENT_PUBLISHABLE_KEY='pk_…' export OTEL_EXPORTER_OTLP_ENDPOINT='https://ingest.aient.ai' export OTEL_EXPORTER_OTLP_PROTOCOL='http/protobuf' export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ${AIENT_PUBLISHABLE_KEY}" export OTEL_SERVICE_NAME='payments-api' export SERVICE_VERSION='2026.08.06.1' export COMMIT_SHA='a full deployed Git SHA' export COMMIT_REF='main' export DEPLOYMENT_ENVIRONMENT='production' export OTEL_RESOURCE_ATTRIBUTES="service.version=${SERVICE_VERSION},deployment.commit_sha=${COMMIT_SHA},deployment.commit_ref=${COMMIT_REF},deployment.environment=${DEPLOYMENT_ENVIRONMENT}" ``` `OTEL_EXPORTER_OTLP_ENDPOINT` is a base URL. Standard OTLP SDKs append `/v1/traces` for traces. `OTEL_EXPORTER_OTLP_HEADERS` makes each export request send `Authorization: Bearer `. Use the immutable version that identifies the deployed build, such as a release tag or build number, for `service.version`. Set `deployment.commit_sha` to the exact revision running in production, not the revision currently checked out by a CI job after deployment. The same `service.name` and commit SHA must be used when you upload source maps. > [!NOTE] > > Aient accepts OTLP/HTTP. Do not use an OTLP/gRPC-only endpoint. If your SDK ignores the shared protocol variables, configure its HTTP/protobuf exporter explicitly, as shown below. ## Python: Django, FastAPI, or Flask Install the HTTP exporter and the instrumentation package for your framework. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-http pip install opentelemetry-instrumentation-django # Or: opentelemetry-instrumentation-fastapi / opentelemetry-instrumentation-flask ``` With the shared deployment values set, launch through the OpenTelemetry wrapper: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} opentelemetry-instrument \ --traces_exporter otlp \ --metrics_exporter none \ gunicorn myproject.wsgi:application ``` For FastAPI or Flask, run the normal ASGI or WSGI command through `opentelemetry-instrument`. The wrapper instruments installed framework packages before your application loads. ## Java: Spring Boot and JVM services Download the [OpenTelemetry Java agent](https://opentelemetry.io/docs/zero-code/java/agent/) as part of your build or container image, then run the service with it. The shared deployment values configure the endpoint, bearer header, service, version, commit, ref, and environment. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} java -javaagent:/app/opentelemetry-javaagent.jar -jar /app/service.jar ``` The Java agent auto-instruments supported frameworks, HTTP clients, JDBC, and more. Keep `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`: the public Aient ingest endpoint is OTLP/HTTP. ## .NET: ASP.NET Core Install the SDK, OTLP exporter, and ASP.NET Core instrumentation. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} dotnet add package OpenTelemetry.Extensions.Hosting dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol dotnet add package OpenTelemetry.Instrumentation.AspNetCore ``` Add OpenTelemetry before building the application. The shared deployment values provide the resource attributes and bearer-authenticated HTTP exporter configuration. ```csharp theme={"theme":{"light":"github-light","dark":"github-dark"}} using OpenTelemetry.Trace; var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenTelemetry() .WithTracing(tracing => tracing .AddAspNetCoreInstrumentation() .AddOtlpExporter()); var app = builder.Build(); app.MapGet("/health", () => Results.Ok()); app.Run(); ``` ## Ruby: Rails Add the OpenTelemetry packages to your Gemfile. ```ruby theme={"theme":{"light":"github-light","dark":"github-dark"}} gem 'opentelemetry-sdk' gem 'opentelemetry-exporter-otlp' gem 'opentelemetry-instrumentation-all' ``` Then initialise tracing before Rails loads the application. The Ruby OTLP exporter reads the shared endpoint, bearer header, and resource environment variables. ```ruby theme={"theme":{"light":"github-light","dark":"github-dark"}} # config/initializers/opentelemetry.rb require 'opentelemetry/sdk' require 'opentelemetry/instrumentation/all' require 'opentelemetry-exporter-otlp' OpenTelemetry::SDK.configure do |config| config.service_name = ENV.fetch('OTEL_SERVICE_NAME') config.use_all end ``` ## PHP: Laravel or Symfony Install the auto-instrumentation package for the framework. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} composer require open-telemetry/opentelemetry-auto-laravel # Or: composer require open-telemetry/opentelemetry-auto-symfony ``` Add these PHP-specific variables alongside the shared deployment values: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} export OTEL_PHP_AUTOLOAD_ENABLED=true export OTEL_TRACES_EXPORTER=otlp ``` PHP loads the exporter and instrumentation from the environment. Keep the shared `OTEL_EXPORTER_OTLP_HEADERS` value intact: it is the bearer token header. ## Go: net/http, Gin, or other HTTP services Install the SDK, HTTP exporter, and the instrumentation package for your router. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} go get go.opentelemetry.io/otel/sdk go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp go get go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin ``` Configure the HTTP exporter and resource before creating your router. This example uses Gin; keep the same resource fields for other routers. ```go theme={"theme":{"light":"github-light","dark":"github-dark"}} ctx := context.Background() exporter, err := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("ingest.aient.ai"), otlptracehttp.WithURLPath("/v1/traces"), otlptracehttp.WithHeaders(map[string]string{ "Authorization": "Bearer " + os.Getenv("AIENT_PUBLISHABLE_KEY"), }), ) if err != nil { log.Fatal(err) } res, err := resource.New(ctx, resource.WithAttributes( semconv.ServiceName(os.Getenv("OTEL_SERVICE_NAME")), attribute.String("service.version", os.Getenv("SERVICE_VERSION")), attribute.String("deployment.commit_sha", os.Getenv("COMMIT_SHA")), attribute.String("deployment.commit_ref", os.Getenv("COMMIT_REF")), attribute.String("deployment.environment", os.Getenv("DEPLOYMENT_ENVIRONMENT")), )) if err != nil { log.Fatal(err) } provider := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(res), ) otel.SetTracerProvider(provider) defer provider.Shutdown(ctx) ``` The snippet requires the normal Go imports for `context`, `log`, `os`, `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/attribute`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/sdk/resource`, `go.opentelemetry.io/otel/sdk/trace`, and your Semantic Conventions package. Add your router middleware after registering the provider. ## Elixir: Phoenix Add OpenTelemetry and Phoenix instrumentation to `mix.exs`, then configure the OTLP exporter in `runtime.exs` before the application starts. ```elixir theme={"theme":{"light":"github-light","dark":"github-dark"}} config :opentelemetry, :resource, service: [name: System.fetch_env!("OTEL_SERVICE_NAME")], "service.version": System.fetch_env!("SERVICE_VERSION"), "deployment.commit_sha": System.fetch_env!("COMMIT_SHA"), "deployment.commit_ref": System.fetch_env!("COMMIT_REF"), "deployment.environment": System.fetch_env!("DEPLOYMENT_ENVIRONMENT") config :opentelemetry, :processors, otel_batch_processor: %{ exporter: {:opentelemetry_exporter, %{endpoints: ["https://ingest.aient.ai/v1/traces"], headers: [{"Authorization", "Bearer #{System.fetch_env!("AIENT_PUBLISHABLE_KEY")}"}]}} } ``` Use `:opentelemetry_phoenix` to instrument Phoenix after the exporter is configured. ## Verify the connection Deploy the instrumented service, make one request that reaches the application, then check Aient's telemetry view for the service name. The trace should show the `service.version`, deployed commit SHA, branch, and environment you set above. If no traces arrive, first confirm that the process received `AIENT_PUBLISHABLE_KEY` and that the exported header is exactly `Authorization: Bearer `. Then confirm the exporter uses `https://ingest.aient.ai` over OTLP/HTTP, not gRPC. See [OpenTelemetry endpoints](../reference/otlp-endpoints) for the endpoint contract. # Server telemetry quickstart Source: https://docs.aient.ai/quickstart/server Install @aient/otel and initialize it before HTTP, database, or queue clients to send Node.js traces and logs to Aient with service name and release metadata. # Server telemetry quickstart Install the server package and initialize it before your application creates HTTP servers, database clients, queues, or framework handlers. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} pnpm add @aient/otel ``` ```ts theme={"theme":{"light":"github-light","dark":"github-dark"}} import { registerOTel } from '@aient/otel' registerOTel({ publishableKey: process.env.AIENT_PUBLISHABLE_KEY, serviceName: 'api', release: { commit: process.env.COMMIT_SHA, branch: process.env.COMMIT_REF, environment: process.env.NODE_ENV ?? 'development', }, }) ``` ## Required values | Value | Source | | - | - | | `publishableKey` | Aient environment key for the target environment | | `serviceName` | Stable name for this runtime, matching source map uploads when applicable | | `release.environment` | Deployment environment slug such as `prod`, `qa`, or `dev` | | `release.commit` | Set `release.commit` explicitly to the full deployed commit SHA (see [release metadata](../reference/sdk-server#release-metadata)) | Send traces and logs to Aient unless you override endpoints for local collector testing. # Event registry Source: https://docs.aient.ai/reference/event-registry Aient publishes webhook event schemas at registry.aient.ai with immutable schema artifact URLs and versioned event aliases for generated clients and browsing. # Event registry Aient publishes webhook schemas as immutable registry artifacts. Event pages link to both the immutable schema ID and the version alias for each public event. ## URL shapes | URL | Meaning | | - | - | | `https://registry.aient.ai/schemas/.json` | Immutable schema artifact | | `https://registry.aient.ai/events//v.json` | Versioned event alias | Use immutable schema URLs in generated clients and version aliases when browsing by event type. # aient-viewer RBAC template Source: https://docs.aient.ai/reference/kubernetes-aient-viewer The read-only RBAC Aient publishes for the viewer of a Kubernetes connection: copy it, remove what you do not want agents to read, and paste it when you attach a cluster. # aient-viewer RBAC template Paste this when you [attach a cluster](/ai-tools/kubernetes-connections#choose-the-rbac-you-declare). It grants `get`, `list` and `watch` on the common built-in and operator resources, plus pod logs. It leaves out Secrets and the resources whose contents can carry secret material; the comments at the top name what is excluded and why. Remove any API group you do not want agents to read. Aient merges the rules of both ClusterRoles into one declaration. The gateway admits only `get` and `list`, whatever `watch` grants. ```yaml aient-viewer.yaml theme={"theme":{"light":"github-light","dark":"github-dark"}} # Generated by infra/k8s-identity/aient-viewer/generate.mjs from policy.yaml and the repository's CRDs. # Do not edit: change policy.yaml and run the generator with --write. # # Excluded API groups: # sandbox.aient.ai: Sandbox create.envVars retains customer credentials; native owners are not shared diagnostic data # helm.cattle.io: a k3s HelmChart carries chart values inline in valuesContent, which can hold credentials; little diagnostic value # authentication.k8s.io: create-only review APIs; nothing to read # authorization.k8s.io: create-only review APIs; nothing to read # Denied resources (their spec or status can carry secret material): # aquasecurity.github.io/exposedsecretreports: each report quotes the secrets Trivy found in an image apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: aient-viewer annotations: aient.ai/viewer-excluded-groups: authentication.k8s.io,authorization.k8s.io,helm.cattle.io,sandbox.aient.ai aient.ai/viewer-denied-resources: aquasecurity.github.io/exposedsecretreports rules: - apiGroups: - "" resources: - componentstatuses - configmaps - endpoints - events - limitranges - namespaces - namespaces/status - nodes - nodes/status - persistentvolumeclaims - persistentvolumeclaims/status - persistentvolumes - persistentvolumes/status - pods - pods/status - podtemplates - replicationcontrollers - replicationcontrollers/scale - replicationcontrollers/status - resourcequotas - resourcequotas/status - serviceaccounts - services - services/status verbs: - get - list - watch - apiGroups: - acme.cert-manager.io - actions.github.com - admissionregistration.k8s.io - apiextensions.k8s.io - apiregistration.k8s.io - apps - argocd-image-updater.argoproj.io - argoproj.io - autoscaling - barmancloud.cnpg.io - batch - cert-manager.io - certificates.k8s.io - cilium.io - clickhouse-keeper.altinity.com - clickhouse.altinity.com - coordination.k8s.io - core.strimzi.io - discovery.k8s.io - events.k8s.io - flowcontrol.apiserver.k8s.io - gateway.envoyproxy.io - gateway.networking.k8s.io - gateway.networking.x-k8s.io - job.min.io - k3s.cattle.io - kafka.strimzi.io - metrics.k8s.io - minio.min.io - networking.k8s.io - node.k8s.io - opentelemetry.io - policy - postgresql.cnpg.io - rbac.authorization.k8s.io - resource.k8s.io - restate.dev - scheduling.k8s.io - spire.spiffe.io - storage.k8s.io - sts.min.io - tailscale.com - upgrade.cattle.io resources: - "*" verbs: - get - list - watch - apiGroups: - aquasecurity.github.io resources: - clustercompliancereports - clustercompliancereports/status - clusterconfigauditreports - clusterinfraassessmentreports - clusterrbacassessmentreports - clustersbomreports - clustervulnerabilityreports - configauditreports - infraassessmentreports - rbacassessmentreports - sbomreports - vulnerabilityreports verbs: - get - list - watch --- # On-demand pod logs, bound separately so a role without them stays possible. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: aient-viewer-logs rules: - apiGroups: - "" resources: - pods/log verbs: - get - list - watch ``` # OpenTelemetry endpoints Source: https://docs.aient.ai/reference/otlp-endpoints Public OTLP HTTP endpoints for traces and logs at ingest.aient.ai, bearer token auth with publishable keys, and resource attributes Aient uses for grouping. # OpenTelemetry endpoints Use the Aient SDKs when possible. If you are using a standard OpenTelemetry SDK directly, send OTLP HTTP traffic to the public ingest endpoints with a publishable key. ## Public endpoints | Signal | Endpoint | Protocol | | - | - | - | | Traces | `https://ingest.aient.ai/v1/traces` | OTLP HTTP/JSON for Aient JS SDKs; standard OTLP HTTP clients may use their supported encoding | | Logs | `https://ingest.aient.ai/v1/logs` | OTLP HTTP/JSON for Aient JS SDKs; standard OTLP HTTP clients may use their supported encoding | ## Authentication Set this header on every request: ```txt theme={"theme":{"light":"github-light","dark":"github-dark"}} Authorization: Bearer ``` The publishable key identifies the organisation and environment. Do not send `organisationId` from application code. ## Resource attributes Set these resource attributes so Aient can group services and resolve source maps: | Attribute | Meaning | | - | - | | `service.name` | Stable service name shown in Aient | | `deployment.commit_sha` | Exact commit for the deployed build | | `deployment.commit_ref` | Branch or ref name | | `deployment.environment` | Environment slug such as `prod`, `qa`, or `dev` | ## Local development For local collectors, use `http://localhost:4318` as the base endpoint and let the SDK derive `/v1/traces` and `/v1/logs`. The Aient JavaScript SDKs send OTLP HTTP JSON payloads and set `Content-Type: application/json`. They do not bundle protobuf runtime packages. # @aient/otel-browser SDK reference Source: https://docs.aient.ai/reference/sdk-browser Configuration options for @aient/otel-browser including publishable key, service name, release metadata, and exporter URL overrides for browser telemetry. # @aient/otel-browser `@aient/otel-browser` sends browser traces and logs to Aient using public OTLP HTTP endpoints. Use a publishable key and release metadata from your build environment. For React or Next.js (client mount, `identify`/`clearUser`, and cleanup), see the [browser quickstart](../quickstart/browser). ## Configuration | Option | Meaning | | - | - | | `publishableKey` | Aient environment publishable key safe for browser code | | `serviceName` | Frontend service name; match source map uploads | | `installationId` | Caller-owned application installation ID, emitted as a resource and untrusted baggage correlation field | | `attributes` | Static resource attributes fixed for the SDK lifetime | | `contextAttributes` | Initial mutable string context for future spans and outgoing baggage | | `release.environment` | Deployment environment slug | | `release.commit` | Build commit SHA; match source map uploads | | `release.branch` | Build branch or ref for release correlation | | `exporterUrl` | Optional base endpoint override; the SDK appends `/v1/traces` and `/v1/logs` | | `exporterHeaders` | Optional custom exporter headers merged with Aient auth | | `logs.exporterUrl` | Optional logs-specific endpoint override | The SDK sends OTLP HTTP JSON payloads. `http/protobuf` is not bundled; legacy protobuf config falls back to JSON. Avoid high-cardinality span names and avoid attaching sensitive user input to attributes. ## Release metadata Pass the full build commit SHA explicitly in `release.commit`, using the same value when uploading source maps. Your build must expose that value to browser code. For Next.js on Vercel, pass `process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA` as shown in the [browser quickstart](../quickstart/browser). For Vite, set `VITE_COMMIT_SHA` when building and pass it explicitly: ```ts theme={"theme":{"light":"github-light","dark":"github-dark"}} registerOTelBrowser({ release: { commit: import.meta.env.VITE_COMMIT_SHA }, }) ``` ## Dynamic context Call `sdk.setContextAttributes()` after a business, account, or workspace switch. The call synchronously replaces the whole snapshot; `null` or an empty object clears it. `sdk.getContextAttributes()` returns a clone. ```ts theme={"theme":{"light":"github-light","dark":"github-dark"}} sdk.setContextAttributes({ 'business.id': 'business_123', 'workspace.id': 'workspace_456', }) // Clear on logout sdk.setContextAttributes(null) ``` Future spans capture the current snapshot at span start; in-flight and ended spans are not changed retroactively. Outgoing Fetch/XHR baggage preserves unrelated entries and replaces SDK-owned keys. All `enduser.*` keys and `app.installation.id` are omitted from generic context. Browser context and baggage are untrusted telemetry correlation data. Never use them for authorization; downstream services must derive identity and access from trusted server-side sessions or credentials. ## User context and baggage Call `sdk.identify({ userId, pseudoId, email, role })` after sign-in to replace the complete user snapshot and tag future browser spans/logs. Call `sdk.clearUser()` on sign-out. `sdk.getIdentity()` returns a clone containing the installation plus current user fields. The legacy user-context methods remain aliases. `app.installation.id`, `enduser.id`, and `enduser.pseudo.id` may propagate as W3C `baggage`. They are correlation hints, not authentication. Email and role do not propagate. Aient backends derive authenticated `enduser.id` from the verified server session and overwrite the browser value before downstream work. Use a stable, non-reversible hash (for example, an HMAC of the user id with a server-side secret) for `pseudoId` so you can correlate sessions without exposing PII. ```ts theme={"theme":{"light":"github-light","dark":"github-dark"}} sdk.identify({ userId: 'user_123', // untrusted baggage correlation pseudoId: 'pseudo_abc', // untrusted baggage correlation email: 'user@example.com', // local span attribute only role: 'admin', // local span attribute only }) ``` # @aient/otel SDK reference Source: https://docs.aient.ai/reference/sdk-server Configuration options for the @aient/otel Node.js package including publishable key, service name, release metadata, and exporter URL and header overrides. # @aient/otel `@aient/otel` configures OpenTelemetry for Node.js services and exports traces and logs to Aient. Initialize it as early as possible in process startup. ## Configuration | Option | Meaning | | - | - | | `publishableKey` | Aient environment publishable key used for OTLP auth | | `serviceName` | Stable service name used in Aient views | | `release.environment` | Deployment environment slug | | `release.commit` | Deployed commit SHA for release and source map correlation | | `release.branch` | Deployed branch or ref for release correlation | | `exporterUrl` | Optional base endpoint override; the SDK appends `/v1/traces` and `/v1/logs` | | `exporterHeaders` | Optional custom exporter headers merged with Aient auth | By default, traces use `https://ingest.aient.ai/v1/traces` and logs use `https://ingest.aient.ai/v1/logs`. The SDK sends OTLP HTTP JSON payloads. Legacy `http/protobuf` protocol values are accepted for compatibility but fall back to JSON. ## Release metadata Pass the full deployed commit SHA explicitly in `release.commit` so Aient can correlate telemetry with the right release. Make the value available to the running service, not only to its build step: ```ts theme={"theme":{"light":"github-light","dark":"github-dark"}} registerOTel({ release: { commit: process.env.COMMIT_SHA }, }) ``` Use the same commit when uploading source maps. Set it from your deployment system or from `git rev-parse HEAD` for the checkout being deployed. # @aient/sourcemaps CLI reference Source: https://docs.aient.ai/reference/sourcemaps-cli Command line options for JavaScript source maps and typed Flutter Dart AOT and Apple dSYM debug artifact uploads. # @aient/sourcemaps CLI Use the `upload` command from CI after generating production source maps. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} aient-sourcemaps upload --service [options] ``` ## Options | Option | Meaning | | - | - | | `` | Build output directory containing `.map` files | | `--api-key`, `-k` | API key, or use `AIENT_API_KEY` | | `--service`, `-s` | Runtime service name | | `--commit`, `-c` | Git commit SHA; auto-detected from common CI env vars when omitted | | `--pattern` | Glob for source maps; defaults to `**/*.js.map` | | `--bundle-prefix` | Optional URL/path prefix prepended to derived bundle paths | | `--api-url` | Optional Aient API URL override | | `--dry-run` | List matched files without uploading | | `--concurrency` | Maximum concurrent uploads | | `--fail-on-empty` | Exit non-zero when no source maps are found | ## Flutter Dart AOT and Apple dSYM debug artifacts Current supported release: `0.3.4`. Use the installed, lockfile-pinned binary from a release-tooling package. The command is Node-based CI tooling; it is not a Flutter runtime dependency and does not scan or infer native build identity from an arbitrary directory. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} pnpm exec aient-sourcemaps upload-debug \ --manifest \ --distribution ``` Manifest items may be `dart_aot_dwarf` or `apple_dsym`. Each Apple item must point to one thin, regular Mach-O DWARF member from a dSYM bundle—not the directory or an archive—and declare the member's exact architecture and Mach-O UUID. The CLI re-validates those values from the file bytes before uploading. | Option | Meaning | | - | - | | `--manifest` | Required strict snake\_case manifest v1 path | | `--distribution` | Required authoritative AAB or IPA described by the manifest | | `AIENT_API_KEY` | Required upload-scoped API key environment variable; keys are not accepted in command arguments | | `--api-url` | Optional Aient API URL, or use `AIENT_API_URL` | | `--dry-run` | Validate and hash local distribution/artifact bytes without network calls | | `--wait` | Rejected until Aient exposes a real processing-status receipt | See [Upload Flutter debug artifacts](../source-maps/flutter-debug-artifacts) for the pinned installation, Android and iOS build flags, native artifact retention, manifest schema, runtime attributes, credential boundary, and release workflow. # Upload Flutter debug artifacts Source: https://docs.aient.ai/source-maps/flutter-debug-artifacts Build, retain, and upload exact Dart AOT symbols and Apple dSYM members for Flutter releases, while preserving R8 mappings for their separate symbolication path. # Upload Flutter debug artifacts Flutter has more than one symbol format. A native release can contain Dart AOT frames, iOS native frames, and Android Java/Kotlin or native frames. Those frames do not use JavaScript source maps and do not share one interchangeable symbol file. `@aient/sourcemaps` is an npm-distributed **build-time CI tool**. Aient uses npm to distribute one versioned, cross-platform command that can validate files and call the upload API from a trusted release runner. It is never linked into your Flutter application, added to `pubspec.yaml`, or run on a phone. Installing it does not inject Node.js or an Aient uploader into the app process. Flutter Web is the exception: it emits JavaScript and uses the regular [source map upload](./upload) flow. ## What Aient accepts today | Runtime frames | Build artifact | Generate and retain? | Accepted by `upload-debug` 0.3.4? | | - | - | - | - | | Obfuscated root-loading-unit Dart AOT on Android | `app.android-.symbols` | Yes | Yes | | Android deferred-component/loading-unit Dart AOT | `app.android-.symbols-.part.so` | Yes | No; retain every loading-unit file | | Obfuscated Dart AOT on iOS | `app.ios-.symbols` | Yes | Yes | | iOS app/framework native code | Thin Mach-O members within `.xcarchive/dSYMs/*.dSYM` | Yes | Yes; one raw DWARF member per manifest item | | Android Java/Kotlin after R8 | `outputs/mapping//mapping.txt` | Yes when minification is enabled | No; retain it for Play/retrace | | Android NDK/native code | ELF/`.so` debug symbols | Yes when your app owns native code | No | The current command accepts `dart_aot_dwarf` and `apple_dsym` manifest items. An Apple item is one thin, raw Mach-O DWARF member inside a dSYM bundle; the bundle directory, ZIP archives, and fat/universal objects are rejected. Android R8/ProGuard mappings and Android native ELF files are not accepted. Generate and retain every build-specific artifact because it generally cannot be recreated after a rebuild. An accepted storage commit proves that Aient retained the exact verified bytes; it does not by itself prove that a later occurrence was symbolicated. ## Install the release tool Keep Node release tooling separate from Flutter runtime dependencies. For example, check in this file as `release-tooling/package.json`: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "name": "mobile-release-tooling", "private": true, "packageManager": "pnpm@10.17.1", "devDependencies": { "@aient/sourcemaps": "0.3.4" } } ``` Create and commit the matching lockfile, then use only the installed, lockfile-pinned binary in release CI: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} cd release-tooling corepack pnpm install --frozen-lockfile corepack pnpm exec aient-sourcemaps --version ``` With npm, the equivalent setup is: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npm install --save-dev --save-exact @aient/sourcemaps@0.3.4 npm ci npm exec -- aient-sourcemaps --version ``` Do not use an unpinned `npx`, `pnpm dlx`, branch URL, or `latest` tag in the authoritative release job. The committed manifest and lockfile are the supply chain record for the uploader version used by that release. ## Keep telemetry and upload credentials separate | Value | Where it belongs | Purpose | | - | - | - | | `AIENT_PUBLISHABLE_KEY` | Flutter build/runtime configuration | Sends OTLP traces and logs from the app. Use the key for the exact dev, QA, staging, or production environment selected by that build. | | `AIENT_API_KEY` | Protected release-CI secret only | Uploads private source-equivalent debug artifacts. Never put it in `--dart-define`, app assets, a mobile binary, or a retained manifest. | | `AIENT_API_URL` | Release CI, optional | Overrides the Aient API origin; omit it for `https://aient.ai`. | Publishable environment keys and the private upload credential are not interchangeable. A development build should use the development publishable key, automated app tests should use QA, staging builds should use staging, and TestFlight/store delivery builds should use production unless they are explicitly a staging distribution. None of those publishable values authorizes a symbol upload. Make one application-owned build intent the source of both the canonical Aient environment slug and its publishable key: | `AIENT_BUILD_INTENT` | Typical use | `deployment.environment` | Publishable key | | - | - | - | - | | `development` | Local and internal development | `dev` | The active key issued by the Aient `dev` environment | | `qa` | Maestro and other automated app tests | `qa` | The active key issued by the Aient `qa` environment | | `staging` | An explicitly staging-targeted distribution | `stg` | The active key issued by the Aient `stg` environment | | `production` | TestFlight, store, and other real delivery builds | `prod` | The active key issued by the Aient `prod` environment | Do not accept an independently selected slug and arbitrary syntactically valid publishable key. Resolve both from the build intent, including for development, and make a protected release fail if an explicit override does not equal the key configured for that intent. Publishable keys may be embedded in a mobile client; the upload-scoped `AIENT_API_KEY` must never be embedded. Configure `AIENT_TELEMETRY_ENABLED` as default-on for every build intent. Only the normalized values `false` and `0` should opt out. Omission, `true`, and malformed values must leave telemetry enabled so a missing CI-only define cannot silently remove release observability. In Aient, open **Settings → API keys → Create Key** and create a key with the `upload` scope. Copy the value when it is shown—the value is displayed only once—and store it in the protected CI secret used by your release workflow (the example below names that secret `AIENT_DEBUG_ARTIFACT_UPLOAD_KEY`). Do not grant `admin` scope, and do not substitute an app's publishable ingest key. The uploader does not proactively print configuration or perform secret redaction. String failure messages are preserved verbatim. Structured non-string failure values use bounded Node.js inspection (depth 8, at most 100 array items, and at most 100,000 characters per string) so pathological objects cannot flood the runner. Keep credentials out of command arguments and user-supplied exception values; CI log hygiene remains the workflow owner's responsibility. ## Create one immutable release identity Before the authoritative build, create one immutable `AIENT_RELEASE_ID`. Pass the same value to the app and to the manifest generated from that exact build. Also capture the exact lowercase 40-character commit SHA, app version, build number, platform, and flavor/variant. Set these resource attributes on Flutter exception telemetry: | Attribute | Value | | - | - | | `service.name` | Stable runtime name matching the Aient service config | | `deployment.commit_sha` | Exact release commit | | `deployment.environment` | Exact runtime environment such as `dev`, `qa`, `stg`, or `prod` | | `aient.release.id` | Immutable pre-build release ID | | `aient.stacktrace.format` | `dart_aot` for obfuscated AOT stacks | | `os.type` | `android` or `ios` | | `host.arch` | `arm64`, `arm`, or `x86_64` | Commit SHA is provenance. Aient selects Dart debug artifacts by exact service, release ID, platform, and architecture, so a rebuild from the same commit needs a new release ID and its own artifacts. ## Android: produce AAB, Dart symbols, and R8 mapping Build the exact AAB that you will distribute. `--obfuscate` renames Dart symbols; `--split-debug-info` writes the matching per-architecture Dart debug files outside the AAB. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} AIENT_DART_SYMBOLS_DIR="build/debug-info/${AIENT_RELEASE_ID}/android" flutter build appbundle \ --release \ --flavor production \ -t lib/main_production.dart \ --obfuscate \ --split-debug-info="${AIENT_DART_SYMBOLS_DIR}" \ --dart-define=AIENT_RELEASE_ID="${AIENT_RELEASE_ID}" \ --dart-define=AIENT_COMMIT_SHA="${GITHUB_SHA}" \ --dart-define=AIENT_ENVIRONMENT=prod \ --dart-define=AIENT_PUBLISHABLE_KEY="${AIENT_PUBLISHABLE_KEY}" ``` Remove `--flavor` and `-t` if the project has no flavors. A flavored project normally produces an AAB below `build/app/outputs/bundle/Release/`; an unflavored Flutter project normally produces it below `build/app/outputs/bundle/release/`. Select one exact file and fail if zero or multiple candidates exist. The split-debug directory contains names such as: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} app.android-arm.symbols app.android-arm64.symbols app.android-x64.symbols ``` Use Aient architecture `x86_64` for Flutter's `x64` filename token. Include every architecture emitted by the distributed build in the manifest. Flutter deferred components can also emit loading-unit files such as `app.android-arm64.symbols-2.part.so`. Version 0.3.4 has no loading-unit identity field and permits only one Android `dart_aot_dwarf` item per architecture, so it can symbolicate only the root loading unit. Do not add loading-unit files to the current manifest. Retain every `*.symbols-*.part.so` file with the distributed build for manual recovery and future uploader support. Flutter release builds run Android's R8 shrinker. If your Android Gradle build minifies Java/Kotlin, preserve the exact mapping produced for the variant: ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}} // android/app/build.gradle.kts android { buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", ) } } } ``` Only enable this when minification is part of your tested release contract. The mapping is build-specific and is overwritten by a later build. ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} build/app/outputs/mapping//mapping.txt ``` For example, a `productionRelease` variant normally writes `build/app/outputs/mapping/productionRelease/mapping.txt`. Confirm the real path in your Android Gradle Plugin version and fail the release if minification is enabled but the file is absent. Do not enable minification solely for Aient while Aient still cannot ingest the mapping; if you already enable it, retain the mapping for Google Play or `retrace`. ## iOS: produce IPA, Dart symbols, and dSYMs For every Release configuration used for distribution, set Xcode's **Debug Information Format** (`DEBUG_INFORMATION_FORMAT`) to **DWARF with dSYM File**. Apple ties each binary to its dSYM by UUID, so a dSYM from a rebuild is not a substitute even when the source commit is unchanged. Build the exact archive and IPA that will be sent to TestFlight or the App Store: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} AIENT_DART_SYMBOLS_DIR="build/debug-info/${AIENT_RELEASE_ID}/ios" flutter build ipa \ --release \ --flavor production \ -t lib/main_production.dart \ --obfuscate \ --split-debug-info="${AIENT_DART_SYMBOLS_DIR}" \ --export-options-plist=ios/ExportOptions.plist \ --dart-define=AIENT_RELEASE_ID="${AIENT_RELEASE_ID}" \ --dart-define=AIENT_COMMIT_SHA="${GITHUB_SHA}" \ --dart-define=AIENT_ENVIRONMENT=prod \ --dart-define=AIENT_PUBLISHABLE_KEY="${AIENT_PUBLISHABLE_KEY}" ``` The build produces four independently important outputs: | Output | Normal location | Use | | - | - | - | | App Store archive | `build/ios/archive/Runner.xcarchive` | Exact app/framework binaries and their native dSYMs | | Distributable IPA | `build/ios/ipa/*.ipa` | Pass this exact file to `--distribution`, then upload the same bytes to App Store Connect | | Dart AOT symbols | The directory passed to `--split-debug-info`, for example `app.ios-arm64.symbols` | Upload to Aient with `upload-debug` | | Native Apple symbols | Thin Mach-O members below `build/ios/archive/Runner.xcarchive/dSYMs/*.dSYM/Contents/Resources/DWARF/` | Upload each member to Aient and retain the complete archive privately | `flutter build ios` is not the authoritative TestFlight artifact: use `flutter build ipa` so the release job owns both the `.xcarchive` and the exact exported IPA. Do not rebuild after manifest validation. Hash the IPA, upload the Dart symbols and selected dSYM DWARF members, and publish that already-built IPA. ## Generate manifest v1 `upload-debug` validates and uploads a manifest; it intentionally does not guess release identity or generate one by scanning an arbitrary build directory. Check in a small release helper (Dart, Node.js, Ruby, or your CI language) that: 1. finds every `.symbols` regular file in the exact split-debug directory and, for iOS, each selected raw DWARF member inside the archive's dSYM bundles; 2. maps Dart filenames to `android|ios` and `arm|arm64|x86_64`, and reads each Apple member's UUID and `arm64|x86_64` architecture from its Mach-O bytes; 3. calculates SHA-256 and byte length for every debug file and the exact AAB or IPA; 4. writes one strict snake\_case manifest beside the symbol files; and 5. fails on empty files, unknown names, duplicates, symlinks, or more than one selected distribution. The current `@aient/sourcemaps` package does not ship a manifest generator. The `tool/aient_debug_manifest.dart` path in the CI example below is an app-owned, checked-in helper—not a command installed by Aient. Keep its implementation and contract tests in the mobile repository so project-specific flavors and output paths remain explicit. Do not replace it with an unsupported build-directory scan or an invented `generate-debug-manifest` CLI command. Every `artifact_path` is relative to the manifest directory. Placing the manifest beside a Dart symbol file lets that path be its basename; an Apple path may descend into a retained dSYM directory as shown below. The CLI rejects absolute, traversing, symlinked, non-regular, hash-mismatched, size-mismatched, wrong-format, and wrong-architecture inputs. Each debug artifact is limited to 100 MiB. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "schema_version": 1, "service": "cope-mobile", "commit_sha": "0123456789abcdef0123456789abcdef01234567", "release_id": "cope-mobile-12345678-1-android", "app_version": "1.2.3", "build_number": "42", "variant": "production", "distribution": { "object_name": "app-production-release.aab", "content_sha256": "<64 lowercase hex characters>", "size_bytes": 12345678 }, "artifacts": [ { "artifact_kind": "dart_aot_dwarf", "platform": "android", "architecture": "arm64", "object_name": "app.android-arm64.symbols", "artifact_path": "app.android-arm64.symbols", "content_sha256": "<64 lowercase hex characters>", "size_bytes": 2345678, "content_type": "application/octet-stream" } ] } ``` Create one manifest per platform/distribution. For iOS, use `platform: "ios"`, the exact IPA basename and hash, and the `app.ios-*.symbols` files from the same build. Add each native member as a separate `apple_dsym` item with its canonical hyphenated Mach-O UUID in `debug_id`. `artifact_path` must point directly to the thin regular file; do not point it at the `.dSYM` directory or an archive: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "artifact_kind": "apple_dsym", "platform": "ios", "architecture": "arm64", "debug_id": "01234567-89ab-cdef-0123-456789abcdef", "object_name": "Runner", "artifact_path": "Runner.dSYM/Contents/Resources/DWARF/Runner", "content_sha256": "<64 lowercase hex characters>", "size_bytes": 3456789, "content_type": "application/octet-stream" } ``` The CLI inspects the bytes and rejects missing or mismatched UUIDs and architectures, archives, fat/universal objects, symlinks, and non-regular files. ## Validate and upload Run a local-only validation before using credentials: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} cd release-tooling corepack pnpm exec aient-sourcemaps upload-debug \ --manifest "../build/debug-info/${AIENT_RELEASE_ID}/android/manifest.json" \ --distribution "../build/app/outputs/bundle/productionRelease/app-production-release.aab" \ --dry-run ``` Then upload from the protected release job: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} cd release-tooling AIENT_API_KEY="${AIENT_API_KEY}" \ corepack pnpm exec aient-sourcemaps upload-debug \ --manifest "../build/debug-info/${AIENT_RELEASE_ID}/android/manifest.json" \ --distribution "../build/app/outputs/bundle/productionRelease/app-production-release.aab" ``` The command exits non-zero for an empty manifest, local validation failure, service lookup failure, rejected upload, incomplete bulk response, storage failure, or commit failure. A success summary distinguishes artifacts accepted and committed to private storage from artifacts explicitly enqueued for processing. `--wait` is rejected because the current API does not expose a truthful per-artifact processing-completion receipt. Use this four-stage evidence ladder instead: | Stage | Receipt | What it proves | | - | - | - | | 1. Local validation | `--dry-run` reports the expected validated artifact count | The manifest, distribution, hashes, formats, and architectures agree locally; no upload occurred | | 2. Storage commit | The upload summary reports the artifact as accepted and committed | Aient accepted the exact bytes into private storage and committed their immutable metadata; processing is not implied | | 3. Durable processing admission | The same summary reports the artifact as enqueued for processing | Aient durably acknowledged the backfill request; successful symbolication is not implied | | 4. Controlled occurrence | A Problem occurrence with the same service, release ID, platform, and architecture resolves the expected Dart file, function, and line with artifact/resolver provenance | The shipped identity, stored artifact, resolver, and Problem projection work end to end | `0 enqueued for processing` means no durable processing admission was acknowledged by that command. It does not invalidate an accepted storage commit, but it is not a backfill or symbolication receipt. Until a truthful processing status API exists, only the controlled occurrence at stage 4 proves usable symbolication. ### Retry without changing identity After an ambiguous or transient upload failure, retry with the exact same manifest, symbol files, and distribution bytes. For the same service, release ID, artifact kind, platform, and architecture: * the same commit SHA, artifact SHA-256, and byte length is an idempotent replay; the server may classify it as `already_committed`, and the CLI counts it as accepted and committed; * a different commit SHA, artifact SHA-256, or byte length is an immutable identity conflict and is not allowed to overwrite the committed artifact; and * rebuilding, even from the same commit, is a new build rather than an upload retry. Give it a new release ID, regenerate the manifest and symbols, and publish only that rebuilt distribution. A CI rerun that rebuilds with a new run-attempt release ID is therefore a new release candidate. A pure upload retry must reuse the retained outputs from the original build without rerunning Flutter. ## Release-CI order Use this order independently for Android and iOS: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} create release identity -> build exact AAB or IPA once with --obfuscate and --split-debug-info -> verify Dart symbols plus native dSYM/R8 outputs that apply -> generate strict manifest and hash the distribution -> upload Dart symbols and, for iOS, thin dSYM DWARF members with the pinned CLI -> retain manifest, Dart symbols, dSYMs/R8 mappings as restricted CI artifacts -> publish the same previously hashed AAB or IPA without rebuilding ``` Run uploads only in protected trusted release jobs. Pull requests and forks can run `--dry-run` against non-production fixtures, but must not receive `AIENT_API_KEY` or production configuration. ### GitHub Actions skeleton This Android example assumes `tool/aient_debug_manifest.dart` is your checked-in manifest helper implementing the five rules above. Keep the helper and uploader version under review like any other release tooling. ```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}} jobs: android-release: runs-on: ubuntu-latest permissions: contents: read env: AIENT_PUBLISHABLE_KEY: ${{ secrets.AIENT_PROD_PUBLISHABLE_KEY }} APP_VERSION: ${{ vars.APP_VERSION }} BUILD_NUMBER: ${{ github.run_number }} steps: - uses: actions/checkout@v4 # Install your repository-pinned Flutter SDK before these steps. - uses: actions/setup-node@v6 with: node-version: "22" - name: Install pinned uploader working-directory: release-tooling run: corepack pnpm install --frozen-lockfile - name: Define immutable build identity run: | echo "AIENT_RELEASE_ID=mobile-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-android" >> "$GITHUB_ENV" echo "AIENT_DART_SYMBOLS_DIR=build/debug-info/mobile-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-android/android" >> "$GITHUB_ENV" - name: Build exact release once run: | flutter build appbundle \ --release \ --build-name="${APP_VERSION}" \ --build-number="${BUILD_NUMBER}" \ --obfuscate \ --split-debug-info="${AIENT_DART_SYMBOLS_DIR}" \ --dart-define=AIENT_RELEASE_ID="${AIENT_RELEASE_ID}" \ --dart-define=AIENT_COMMIT_SHA="${GITHUB_SHA}" \ --dart-define=AIENT_ENVIRONMENT=prod \ --dart-define=AIENT_PUBLISHABLE_KEY="${AIENT_PUBLISHABLE_KEY}" - name: Generate strict manifest run: | dart run tool/aient_debug_manifest.dart \ --symbols-directory "${AIENT_DART_SYMBOLS_DIR}" \ --distributable build/app/outputs/bundle/release/app.aab \ --service mobile \ --commit "${GITHUB_SHA}" \ --release-id "${AIENT_RELEASE_ID}" \ --platform android \ --app-version "${APP_VERSION}" \ --build-number "${BUILD_NUMBER}" \ --variant production \ --output "${AIENT_DART_SYMBOLS_DIR}/manifest.json" - name: Validate and upload Dart symbols working-directory: release-tooling env: AIENT_API_KEY: ${{ secrets.AIENT_DEBUG_ARTIFACT_UPLOAD_KEY }} run: | corepack pnpm exec aient-sourcemaps upload-debug \ --manifest "../${AIENT_DART_SYMBOLS_DIR}/manifest.json" \ --distribution ../build/app/outputs/bundle/release/app.aab \ --dry-run corepack pnpm exec aient-sourcemaps upload-debug \ --manifest "../${AIENT_DART_SYMBOLS_DIR}/manifest.json" \ --distribution ../build/app/outputs/bundle/release/app.aab - name: Retain private recovery artifacts if: always() uses: actions/upload-artifact@v7 with: name: android-debug-artifacts if-no-files-found: error retention-days: 90 path: | build/debug-info/** build/app/outputs/bundle/release/app.aab build/app/outputs/mapping/**/mapping.txt # Publish the already-built, previously validated AAB here. Do not rebuild. ``` Use a separate `macos` job for iOS. Replace the build with the `flutter build ipa` command above, pass the one selected `build/ios/ipa/*.ipa` to the manifest and uploader, and retain `build/debug-info/**`, the complete `build/ios/archive/Runner.xcarchive/**`, and the exact `build/ios/ipa/*.ipa` before publishing that same IPA to App Store Connect. The full archive—not only its dSYM directory—is the authoritative recovery unit containing the matching binaries and UUID-bound dSYMs. ## Troubleshooting * **No `.symbols` files:** both `--obfuscate` and `--split-debug-info=` must be on the release build command. Confirm the directory is unique to this release and platform. * **Distribution mismatch:** the file basename, SHA-256, and byte length must match `distribution`. Upload the exact file used to create the manifest and do not rebuild it. * **Architecture mismatch:** do not infer architecture from the runner. Map the Flutter filename, then let the CLI inspect the ELF or Mach-O bytes. * **Artifact missing in Aient:** confirm runtime `service.name`, `aient.release.id`, `os.type`, and `host.arch` exactly match the manifest. * **Missing or rejected dSYM member:** verify Release uses DWARF with dSYM File, inspect the archive's `dSYMs` directory, and select the thin regular file under `Contents/Resources/DWARF/` rather than the bundle directory or an archive. * **Missing R8 mapping:** verify whether the exact Gradle variant enables minification and inspect its `outputs/mapping//` directory. A mapping from another build cannot be substituted. Primary platform references: [Flutter obfuscation and split debug information](https://docs.flutter.dev/deployment/obfuscate), [Flutter iOS release archives](https://docs.flutter.dev/deployment/ios), [Flutter Android release builds](https://docs.flutter.dev/deployment/android), [Apple dSYM build information](https://developer.apple.com/documentation/xcode/building-your-app-to-include-debugging-information), and [Android R8 mapping files](https://developer.android.com/topic/performance/app-optimization/troubleshoot-the-optimization). # Upload source maps Source: https://docs.aient.ai/source-maps/upload Upload JavaScript source maps from CI with @aient/sourcemaps so Aient resolves minified browser stack frames using matching service name and commit SHA. # Upload source maps Upload source maps from CI after building your frontend. The upload commit and service name must match the telemetry emitted by the deployed bundle. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} pnpm dlx @aient/sourcemaps upload .next/static \ --service web \ --commit "$GITHUB_SHA" \ --bundle-prefix /_next/static \ --fail-on-empty ``` Set `AIENT_API_KEY` in CI. Unlike telemetry publishable keys, source map upload keys are server-side credentials and must not be shipped to browsers. ## Matching rules | Field | Must match | | - | - | | `service` | Browser telemetry `service.name` | | `commit` | Browser telemetry `deployment.commit_sha` | | `bundle-prefix` | Public path that appears in minified stack frame URLs | # Send Vercel logs & traces to Aient with a Drain Source: https://docs.aient.ai/vercel/drain Connect a Vercel custom-endpoint Drain to Aient to ingest Vercel function logs and OTLP traces with near-zero instrumentation. Aient turns crash-class errors into reviewed GitHub fix PRs — reducing MTTR with automated root cause analysis. # Send Vercel logs & traces to Aient with a Drain A Vercel **Drain** forwards your project's observability data to any HTTPS endpoint. Point one at Aient and your Vercel function logs and traces flow straight into Aient's detection and remediation loop — no SDK to install, no code to change. Aient reads the stack traces inside your Vercel function errors, groups them into problems, finds the root cause against your source code, and opens a reviewed GitHub pull request with the fix. The Drain is the fastest way to get there: near-zero instrumentation, and it reuses telemetry Vercel already collects. > [!NOTE] > > A Drain carries **one data type each**. To send both logs and traces, you add two > Drains — one for **Logs**, one for **Traces** — each pointed at its own Aient URL. ## What you get * **Logs → crash-class problems.** Vercel function logs (`lambda`, `edge`, `build`, and more) arrive in Aient. Error- and fatal-level records surface as problems, and the stack traces inside them feed automated root cause analysis. * **Traces → distributed context.** Vercel sends traces as OpenTelemetry over OTLP/HTTP. Aient ingests them as-is, keeping the project and deployment context Vercel attaches. * **Closed-loop remediation.** Once a crash-class problem is detected, Aient investigates and opens a GitHub pull request with the fix. You review and merge — Aient never merges for you. ## Prerequisites * A Vercel project emitting logs and/or traces. * A Vercel **Pro** or **Enterprise** plan. Per [Vercel's Drains docs](https://vercel.com/docs/drains#usage-and-pricing), Drains are available on Pro and Enterprise; Hobby and Pro Trial plans must upgrade to Pro first. Vercel bills Drains volume separately on its side. * An Aient account with an environment to receive the data. * A per-environment **Drain URL** and **Signature Verification Secret** from Aient (see [Get your Drain URL and secret](#get-your-drain-url-and-secret)). ## Get your Drain URL and secret In Aient, go to **Settings → Environments**, open the environment you want Vercel data to land in, and select the **Vercel Drains** tab. Click **Add drain**, choose the **data type** (**Logs** or **Traces**), and create it. Aient then shows you, **once**: * A **Drain URL** — a per-organisation, per-data-type endpoint on the Aient ingest domain (`ingest.aient.ai`), served by the native Vercel-drain receiver. It looks like: ```txt theme={"theme":{"light":"github-light","dark":"github-dark"}} https://ingest.aient.ai/vercel//logs https://ingest.aient.ai/vercel//traces ``` > [!WARNING] > > The Drain URL contains a secret token. Treat the whole URL as a credential: send it > over HTTPS only, never commit it, and never paste it into a public issue. * A **Signature Verification Secret** — an HMAC secret you paste into Vercel's "Signature Verification Secret" field. Aient uses it to verify that every Drain request genuinely came from your Vercel project before accepting the data. > [!WARNING] > > **The signing secret is shown once and cannot be retrieved later.** Copy both the URL > and the secret before closing the dialog. If you lose the secret, use **Rotate** on the > drain to mint a fresh URL + secret pair (and update Vercel with the new values). You can > also **Revoke** a drain — a confirmation-gated action that immediately stops accepting > its traffic. Create one drain for **Logs** and one for **Traces** (they are separate drains with separate tokens), then copy each pair before you start the Vercel steps below. ## Set up the Drain in Vercel In your Vercel dashboard, go to **Settings → Drains → Add Drain** and follow the three steps. Repeat the whole flow once for **Logs** and once for **Traces**. ### Step 1 — Choose the data type Pick **Logs** or **Traces**. These are the two types Aient ingests. ![Vercel Add Drain dialog at step one, Choose data to drain, with Logs, Traces, Speed Insights and Web Analytics options; Traces is selected.](https://mintcdn.com/aient/3S6jKZ6uT7BZLUWl/images/vercel-drains/01-choose-data-type.png?fit=max\&auto=format\&n=3S6jKZ6uT7BZLUWl\&q=85\&s=53c8ce1209bdc22a070e8bf3da17f7e5) Vercel's Add Drain dialog — step 1, choosing the data type to drain. > [!NOTE] > > **Speed Insights** and **Web Analytics** are **not** supported by Aient — they are not > error telemetry and carry no crash signal. Choose only **Logs** or **Traces**. Click **Next**. ### Step 2 — Configure the drain Name the drain, choose which **projects** send data, and optionally add **sampling rules** per environment. ![Vercel Add Drain dialog at step two, Configure the drain, showing drain name, project selection, and a sampling rule with environment, rate and request path prefix.](https://mintcdn.com/aient/3S6jKZ6uT7BZLUWl/images/vercel-drains/02-configure-drain.png?fit=max\&auto=format\&n=3S6jKZ6uT7BZLUWl\&q=85\&s=b82c41f7089255ae2d19131fe368d205) Vercel's Add Drain dialog — step 2, configuring projects, environments and sampling. Recommended settings: * **Projects** — start with the specific projects you want Aient watching. * **Environment** — start with `production`. Add `preview` later if you want preview deployments covered too. * **Sampling** — leave sampling at **100%** for launch so nothing is dropped while you confirm the connection works. Once telemetry is flowing, you can lower the rate (for example to 10%) and pin noisy paths like `/docs` to a low percentage. > [!WARNING] > > **Sampling rules drop non-matching requests silently.** With zero rules, Vercel > forwards 100%. As soon as you add **any** rule, requests that don't match a rule are > dropped. Rules are evaluated top-to-bottom, first match wins. Start at 100% and tighten > deliberately. Click **Next**. ### Step 3 — Configure the destination (Custom Endpoint) Choose **Custom Endpoint** and fill in: ![Vercel Add Drain dialog at step three, Configure the destination, with the Custom Endpoint tab selected showing a POST URL field, JSON encoding, a Test button, and a Signature Verification Secret field.](https://mintcdn.com/aient/3S6jKZ6uT7BZLUWl/images/vercel-drains/03-custom-endpoint.png?fit=max\&auto=format\&n=3S6jKZ6uT7BZLUWl\&q=85\&s=e74999cb20e1125f76cc263bd48c8078) Vercel's Add Drain dialog — step 3, configuring a Custom Endpoint destination with URL, encoding, Test, and Signature Verification Secret. 1. **URL** — paste the Aient **Drain URL** for this data type (the `…/logs` URL on a Logs drain, the `…/traces` URL on a Traces drain). 2. **Encoding** — for **Traces**, either `JSON` or `Protobuf` works; Aient accepts both. For **Logs**, use Vercel's default. 3. **Signature Verification Secret** — paste the Aient **Signature Verification Secret**. \[!TIP] Always set the secret. It lets Aient verify each request and reject anything that isn't from your Vercel project. 4. Click **Test**. Vercel sends a sample request to the Aient URL; a success confirms the URL and secret are correct. 5. Click **Create Drain**. That's it for one data type. Repeat steps 1–3 for the other Drain (Logs or Traces) using its matching Aient URL. ## What to expect in Aient Once the Drain is live and your app produces telemetry: 1. **Telemetry arrives.** Logs and traces land in Aient, stamped to your organisation and environment. Back in **Settings → Environments → Vercel Drains**, each drain shows a health indicator — **Receiving** once valid data is flowing, or **Signature failing** if requests arrive but the signature doesn't verify (usually a stale secret in Vercel — rotate and re-paste). 2. **Problems appear.** Error- and fatal-level logs, and trace errors, are grouped into problems — the same pipeline as Aient's SDK ingest. No extra setup. 3. **Root cause + fix PR.** For crash-class problems that resolve to a stack frame in a repo Aient can read, Aient investigates the root cause and opens a reviewed GitHub pull request. You review and merge; Aient then watches production to confirm the fix held, and reopens the problem if it regresses. > [!NOTE] > > **A note on symbolication.** Vercel log records carry no git commit SHA, so log-sourced > exceptions may symbolicate less precisely than your trace or SDK telemetry. Aient still > ingests them and still produces problems; it just degrades gracefully to raw frames when > the SHA is absent. For the sharpest stack traces, [upload source maps](/source-maps/upload) > and lean on trace ingestion, where deployment context is preserved. ## FAQ > [accordion] Do I need to install an SDK to use a Vercel Drain? > > No. A Drain forwards telemetry Vercel already collects. This is the near-zero > instrumentation path: configure the Drain in Vercel, paste the Aient URL and secret, > and data flows. You can still add the Aient SDK later for richer, app-level spans. > [accordion] Logs vs. Traces — which Drain should I create? > > Create both for full coverage. **Logs** carry your Vercel function output, including > error and fatal records that surface crash-class problems. **Traces** carry > OpenTelemetry spans with distributed context. Each is a separate Drain because Vercel > allows one data type per drain. > [accordion] Why does Aient verify a signature? > > Your Drain URL identifies your organisation, and the Signature Verification Secret > proves each request actually came from your Vercel project. Vercel signs every request > with an **HMAC-SHA1 of the raw request body** and sends it in the `x-vercel-signature` > header; Aient recomputes that HMAC with your secret and rejects anything that doesn't > match (a constant-time compare, fail-closed) — so a leaked URL alone can't be used to > inject telemetry. > [accordion] What wire format does the data arrive in? > > **Logs** arrive as newline-delimited / concatenated JSON objects (one log record per > line) and Aient normalises them into OpenTelemetry log records. **Traces** arrive as > OpenTelemetry over OTLP/HTTP — in `JSON` or `Protobuf`, your choice — and Aient passes > them through, keeping the `vercel.projectId` and `vercel.deploymentId` context Vercel > attaches. > [accordion] Will this reduce my MTTR? > > That's the goal. Aient collapses the slow part of an incident — triage and writing > the fix — into an automated root cause analysis that ends in a ready-to-review pull > request. The Drain removes the instrumentation step, so a Vercel app can reach > closed-loop AI incident management quickly. > [accordion] Can I control how much data Vercel sends? > > Yes, entirely from Vercel. Scope Drains per project and per environment, choose which > log sources to forward, and use sampling rules to cap volume. Start at 100% to confirm > the connection, then tighten. Remember that once any sampling rule exists, non-matching > requests are dropped. > [accordion] Does Aient support Speed Insights or Web Analytics drains? > > No. Aient ingests **Logs** and **Traces** only — those carry the error signal Aient > acts on. Speed Insights and Web Analytics are out of scope. ## Related * [OpenTelemetry endpoints](/reference/otlp-endpoints) — the OTLP ingest surface behind Aient. * [Server telemetry quickstart](/quickstart/server) — add the Aient SDK for richer spans. * [Upload source maps](/source-maps/upload) — resolve minified frames to original source. # instrumentation.discovery_started Source: https://docs.aient.ai/webhooks/events/ai.aient.instrumentation.discovery_started Webhook schema for ai.aient.instrumentation.discovery_started # ai.aient.instrumentation.discovery\_started > Generated from `@aient/events`. Do not edit this page by hand. Instrumentation discovery started for an organisation or repository. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.instrumentation` | | Bounded context | `instrumentation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `instrumentation/discovery` | | Schema ID | `sch_01KREWXD59TKP6P4YZBR6ZSDH9` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD59TKP6P4YZBR6ZSDH9.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.instrumentation.discovery_started/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `agentRole` | no | `string` | - | | `organisationId` | yes | `string` | - | | `repositoryFullName` | no | `string` | - | | `repositoryId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "agentRole": "example-agentRole", "organisationId": "11111111-1111-4111-8111-111111111111", "repositoryFullName": "aient/example", "repositoryId": "repository_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD59TKP6P4YZBR6ZSDH9.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "instrumentation/discovery", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.instrumentation.discovery_started" } ``` # instrumentation.pr_merged Source: https://docs.aient.ai/webhooks/events/ai.aient.instrumentation.pr_merged Webhook schema for ai.aient.instrumentation.pr_merged # ai.aient.instrumentation.pr\_merged > Generated from `@aient/events`. Do not edit this page by hand. An Aient instrumentation pull request was merged. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.instrumentation` | | Bounded context | `instrumentation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `instrumentation/repositories/{repositoryId}/pull-requests` | | Schema ID | `sch_01KREWXD59TKP6P4YZBR6ZSDHA` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD59TKP6P4YZBR6ZSDHA.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.instrumentation.pr_merged/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `agentRole` | no | `string` | - | | `mergedAt` | yes | `string` | - | | `organisationId` | yes | `string` | - | | `pullRequestNumber` | yes | `integer` | - | | `repositoryFullName` | yes | `string` | - | | `repositoryId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "agentRole": "example-agentRole", "mergedAt": "2026-04-25T12:00:00.000Z", "organisationId": "11111111-1111-4111-8111-111111111111", "pullRequestNumber": 1, "repositoryFullName": "aient/example", "repositoryId": "repository_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD59TKP6P4YZBR6ZSDHA.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "instrumentation/repositories/repository_example/pull-requests", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.instrumentation.pr_merged" } ``` # instrumentation.services_selected Source: https://docs.aient.ai/webhooks/events/ai.aient.instrumentation.services_selected Webhook schema for ai.aient.instrumentation.services_selected # ai.aient.instrumentation.services\_selected > Generated from `@aient/events`. Do not edit this page by hand. Instrumentation discovery selected services for a repository. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.instrumentation` | | Bounded context | `instrumentation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `instrumentation/repositories/{repositoryId}/services` | | Schema ID | `sch_01KREWXD59TKP6P4YZBR6ZSDHB` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD59TKP6P4YZBR6ZSDHB.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.instrumentation.services_selected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `agentRole` | no | `string` | - | | `organisationId` | yes | `string` | - | | `repositoryId` | yes | `string` | - | | `selectedServiceNames` | no | `array` | - | | `serviceCount` | yes | `integer` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "agentRole": "example-agentRole", "organisationId": "11111111-1111-4111-8111-111111111111", "repositoryId": "repository_example", "selectedServiceNames": [], "serviceCount": 1 }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD59TKP6P4YZBR6ZSDHB.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "instrumentation/repositories/repository_example/services", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.instrumentation.services_selected" } ``` # integration.github.connected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.github.connected Webhook schema for ai.aient.integration.github.connected # ai.aient.integration.github.connected > Generated from `@aient/events`. Do not edit this page by hand. A GitHub installation became usable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/github/installations/{installationId}` | | Schema ID | `sch_01KREWXD5AMEPBQ7TWS9TK5H8K` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8K.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.github.connected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `accountLogin` | no | `string` | - | | `accountType` | no | `string` | `user`, `organisation` | | `installationId` | yes | `string` | - | | `organisationId` | yes | `string` | - | | `repositorySelection` | no | `string` | `all`, `selected` | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "accountLogin": "aient-example", "accountType": "user", "installationId": "1234", "organisationId": "11111111-1111-4111-8111-111111111111", "repositorySelection": "all" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8K.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/github/installations/1234", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.github.connected" } ``` # integration.github.disconnected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.github.disconnected Webhook schema for ai.aient.integration.github.disconnected # ai.aient.integration.github.disconnected > Generated from `@aient/events`. Do not edit this page by hand. A GitHub installation became unusable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/github/installations/{installationId}` | | Schema ID | `sch_01KREWXD5AMEPBQ7TWS9TK5H8M` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8M.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.github.disconnected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `accountLogin` | no | `string` | - | | `installationId` | yes | `string` | - | | `organisationId` | yes | `string` | - | | `reason` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "accountLogin": "aient-example", "installationId": "1234", "organisationId": "11111111-1111-4111-8111-111111111111", "reason": "example-reason" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8M.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/github/installations/1234", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.github.disconnected" } ``` # integration.linear.connected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.linear.connected CloudEvent fired when a Linear workspace connection becomes usable, with payload fields for organisationId, workspaceId, and workspaceName. # ai.aient.integration.linear.connected > Generated from `@aient/events`. Do not edit this page by hand. A Linear workspace connection became usable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/linear/{workspaceId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5V98` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V98.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.linear.connected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `workspaceId` | no | `string` | - | | `workspaceName` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "workspaceId": "workspace_example", "workspaceName": "Example workspace" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V98.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/linear/workspace_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.linear.connected" } ``` # integration.linear.disconnected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.linear.disconnected Webhook schema for ai.aient.integration.linear.disconnected # ai.aient.integration.linear.disconnected > Generated from `@aient/events`. Do not edit this page by hand. A Linear workspace connection became unusable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/linear/{workspaceId}` | | Schema ID | `sch_01KT52QVFFKAG4PG7Q9BYEW344` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KT52QVFFKAG4PG7Q9BYEW344.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.linear.disconnected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `reason` | no | `string` | - | | `workspaceId` | no | `string` | - | | `workspaceName` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "reason": "example-reason", "workspaceId": "workspace_example", "workspaceName": "Example workspace" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KT52QVFFKAG4PG7Q9BYEW344.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/linear/workspace_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.linear.disconnected" } ``` # integration.mcp.connected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.mcp.connected Webhook schema for ai.aient.integration.mcp.connected # ai.aient.integration.mcp.connected > Generated from `@aient/events`. Do not edit this page by hand. An MCP client connection became usable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/mcp/{connectionKind}` | | Schema ID | `sch_01KREWXD5AMEPBQ7TWS9TK5H8P` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8P.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.mcp.connected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `clientName` | no | `string` | - | | `connectionKind` | yes | `string` | - | | `organisationId` | yes | `string` | - | | `userId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "clientName": "Example name", "connectionKind": "example-connectionKind", "organisationId": "11111111-1111-4111-8111-111111111111", "userId": "user_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8P.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/mcp/example-connectionKind", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.mcp.connected" } ``` # integration.scm.connected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.scm.connected Webhook schema for ai.aient.integration.scm.connected # ai.aient.integration.scm.connected > Generated from `@aient/events`. Do not edit this page by hand. A source control provider connection became usable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/scm/{providerConnectionId}` | | Schema ID | `sch_01KREWXD5AMEPBQ7TWS9TK5H8Q` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8Q.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.scm.connected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `accountLogin` | no | `string` | - | | `organisationId` | yes | `string` | - | | `provider` | yes | `string` | - | | `providerConnectionId` | yes | `string` | - | | `repositorySelection` | no | `string` | `all`, `selected` | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "accountLogin": "aient-example", "organisationId": "11111111-1111-4111-8111-111111111111", "provider": "example-provider", "providerConnectionId": "provider_connection_example", "repositorySelection": "all" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8Q.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/scm/provider_connection_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.scm.connected" } ``` # integration.scm.disconnected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.scm.disconnected Webhook schema for ai.aient.integration.scm.disconnected # ai.aient.integration.scm.disconnected > Generated from `@aient/events`. Do not edit this page by hand. A source control provider connection became unusable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/scm/{providerConnectionId}` | | Schema ID | `sch_01KREWXD5AMEPBQ7TWS9TK5H8R` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8R.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.scm.disconnected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `accountLogin` | no | `string` | - | | `organisationId` | yes | `string` | - | | `provider` | yes | `string` | - | | `providerConnectionId` | yes | `string` | - | | `reason` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "accountLogin": "aient-example", "organisationId": "11111111-1111-4111-8111-111111111111", "provider": "example-provider", "providerConnectionId": "provider_connection_example", "reason": "example-reason" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8R.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/scm/provider_connection_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.scm.disconnected" } ``` # integration.slack.connected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.slack.connected CloudEvent fired when a Slack workspace connection becomes usable for Aient notifications, with payload fields organisationId, workspaceId, and workspaceName. # ai.aient.integration.slack.connected > Generated from `@aient/events`. Do not edit this page by hand. A Slack workspace connection became usable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/slack/{workspaceId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5V94` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V94.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.slack.connected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `workspaceId` | no | `string` | - | | `workspaceName` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "workspaceId": "workspace_example", "workspaceName": "Example workspace" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V94.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/slack/workspace_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.slack.connected" } ``` # integration.slack.disconnected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.slack.disconnected Webhook schema for ai.aient.integration.slack.disconnected # ai.aient.integration.slack.disconnected > Generated from `@aient/events`. Do not edit this page by hand. A Slack workspace connection became unusable. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/slack/{teamId}` | | Schema ID | `sch_01KREWXD5AMEPBQ7TWS9TK5H8S` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8S.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.slack.disconnected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `reason` | no | `string` | - | | `teamId` | yes | `string` | - | | `teamName` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "reason": "example-reason", "teamId": "team_example", "teamName": "Example name" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5AMEPBQ7TWS9TK5H8S.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/slack/team_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.slack.disconnected" } ``` # integration.slack.notification_channel_selected Source: https://docs.aient.ai/webhooks/events/ai.aient.integration.slack.notification_channel_selected CloudEvent fired when a tenant selects a Slack channel to receive Aient notifications, with payload fields channelId, channelName, and organisationId. # ai.aient.integration.slack.notification\_channel\_selected > Generated from `@aient/events`. Do not edit this page by hand. A Slack notification channel was selected. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.integrations` | | Bounded context | `integration` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `integrations/slack/channels/{channelId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5V96` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V96.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.integration.slack.notification_channel_selected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `channelId` | yes | `string` | - | | `channelName` | no | `string` | - | | `organisationId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "channelId": "channel_example", "channelName": "Example name", "organisationId": "11111111-1111-4111-8111-111111111111" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V96.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "integrations/slack/channels/channel_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.integration.slack.notification_channel_selected" } ``` # onboarding.completed Source: https://docs.aient.ai/webhooks/events/ai.aient.onboarding.completed CloudEvent fired when a tenant finishes Aient onboarding, with payload fields organisationId and onboardingRunId emitted from the onboarding bounded context. # ai.aient.onboarding.completed > Generated from `@aient/events`. Do not edit this page by hand. The tenant completed onboarding. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.onboarding` | | Bounded context | `onboarding` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `onboarding-runs/{onboardingRunId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5V92` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V92.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.onboarding.completed/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `onboardingRunId` | no | `string` | - | | `organisationId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "onboardingRunId": "onboarding_run_example", "organisationId": "11111111-1111-4111-8111-111111111111" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V92.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "onboarding-runs/onboarding_run_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.onboarding.completed" } ``` # organisation.created Source: https://docs.aient.ai/webhooks/events/ai.aient.organisation.created CloudEvent fired when a tenant organisation is created in Aient, with payload fields organisationId, organisationName, and ownerUserId for provisioning. # ai.aient.organisation.created > Generated from `@aient/events`. Do not edit this page by hand. A tenant organisation was created. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.organisations` | | Bounded context | `organisation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `organisations/{organisationId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5V90` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V90.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.organisation.created/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `organisationName` | no | `string` | - | | `ownerUserId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "organisationName": "Example name", "ownerUserId": "owner_user_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V90.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "organisations/11111111-1111-4111-8111-111111111111", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.organisation.created" } ``` # problem.closed Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.closed CloudEvent fired when an Aient problem is closed, with payload fields organisationId, problemId, and closureReason for tracking lifecycle outcomes. # ai.aient.problem.closed > Generated from `@aient/events`. Do not edit this page by hand. A problem was closed. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5VA3` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA3.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.closed/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `closureReason` | yes | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "closureReason": "example", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA3.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.closed" } ``` # problem.detected Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.detected CloudEvent fired when Aient detects a new problem from grouped telemetry, with payload fields organisationId, problemId, environmentId, severity, and title. # ai.aient.problem.detected > Generated from `@aient/events`. Do not edit this page by hand. A new problem was detected. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5VB9` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VB9.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.detected/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `environmentId` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `severity` | no | `string` | `critical`, `high`, `medium`, `low` | | `title` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "environmentId": "environment_example", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "severity": "critical", "title": "Example problem" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VB9.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.detected" } ``` # problem.duplicate_changed Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.duplicate_changed Webhook schema for ai.aient.problem.duplicate_changed # ai.aient.problem.duplicate\_changed > Generated from `@aient/events`. Do not edit this page by hand. A problem duplicate relationship changed. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01KSGHAFQTHQZWYVVKJ6C5CTMM` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KSGHAFQTHQZWYVVKJ6C5CTMM.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.duplicate_changed/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `canonicalProblemId` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `reason` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "canonicalProblemId": "canonical_problem_example", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "reason": "example-reason" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KSGHAFQTHQZWYVVKJ6C5CTMM.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.duplicate_changed" } ``` # problem.regressed Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.regressed CloudEvent fired when a previously closed Aient problem regresses, with payload fields organisationId, problemId, and previousStatus for routing. # ai.aient.problem.regressed > Generated from `@aient/events`. Do not edit this page by hand. A previously closed problem regressed. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5VA4` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA4.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.regressed/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `previousStatus` | no | `string` | - | | `problemId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "previousStatus": "example", "problemId": "problem_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA4.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.regressed" } ``` # problem.remediation_decision_recorded Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.remediation_decision_recorded Webhook schema for ai.aient.problem.remediation_decision_recorded # ai.aient.problem.remediation\_decision\_recorded > Generated from `@aient/events`. Do not edit this page by hand. A remediation admission decision was recorded for a problem. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}/remediation-decisions/{decisionId}` | | Schema ID | `sch_01KT542Q05SG5DDF3P83RRP553` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KT542Q05SG5DDF3P83RRP553.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.remediation_decision_recorded/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `action` | yes | `string` | - | | `decision` | yes | `string` | `allowed`, `skipped`, `blocked`, `rejected`, `already_active` | | `decisionId` | yes | `string` | - | | `existingRemediationAttemptId` | no | `string` | - | | `nextReconsiderAt` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `reason` | yes | `string` | - | | `remediationAttemptId` | no | `string` | - | | `stage` | no | `string` | - | | `workRequestId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "action": "start_remediation", "decision": "allowed", "decisionId": "decision_example", "existingRemediationAttemptId": "existing_remediation_attempt_example", "nextReconsiderAt": "2026-04-25T12:00:00.000Z", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "reason": "example-reason", "remediationAttemptId": "remediation_attempt_example", "stage": "example-stage", "workRequestId": "work_request_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KT542Q05SG5DDF3P83RRP553.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example/remediation-decisions/decision_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.remediation_decision_recorded" } ``` # problem.reopened Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.reopened Webhook schema for ai.aient.problem.reopened # ai.aient.problem.reopened > Generated from `@aient/events`. Do not edit this page by hand. A previously closed problem was reopened. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01KSGHAFQVD8J2J96VH1WG9R54` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KSGHAFQVD8J2J96VH1WG9R54.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.reopened/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `previousStatus` | no | `string` | - | | `problemId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "previousStatus": "example", "problemId": "problem_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KSGHAFQVD8J2J96VH1WG9R54.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.reopened" } ``` # problem.suppression_changed Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.suppression_changed Webhook schema for ai.aient.problem.suppression_changed # ai.aient.problem.suppression\_changed > Generated from `@aient/events`. Do not edit this page by hand. A problem alert suppression overlay changed. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01KSGHAFQVD8J2J96VH1WG9R55` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KSGHAFQVD8J2J96VH1WG9R55.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.suppression_changed/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `isMuted` | yes | `boolean` | - | | `note` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `reason` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "isMuted": true, "note": "example-note", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "reason": "example-reason" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KSGHAFQVD8J2J96VH1WG9R55.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.suppression_changed" } ``` # problem.triaged Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.triaged CloudEvent fired when Aient triages a detected problem, with payload fields organisationId, problemId, triageId, and severity for downstream routing. # ai.aient.problem.triaged > Generated from `@aient/events`. Do not edit this page by hand. A detected problem was triaged. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5VA2` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA2.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.triaged/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `severity` | no | `string` | `critical`, `high`, `medium`, `low` | | `triageId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "severity": "critical", "triageId": "triage_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA2.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.triaged" } ``` # problem.verification_started Source: https://docs.aient.ai/webhooks/events/ai.aient.problem.verification_started Webhook schema for ai.aient.problem.verification_started # ai.aient.problem.verification\_started > Generated from `@aient/events`. Do not edit this page by hand. A problem entered production verification. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.problems` | | Bounded context | `problem` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}` | | Schema ID | `sch_01KSGHAFQVD8J2J96VH1WG9R56` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KSGHAFQVD8J2J96VH1WG9R56.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.problem.verification_started/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `reason` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "reason": "example-reason" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KSGHAFQVD8J2J96VH1WG9R56.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.problem.verification_started" } ``` # remediation.investigation_completed Source: https://docs.aient.ai/webhooks/events/ai.aient.remediation.investigation_completed CloudEvent fired when a remediation investigation completes, with payload fields problemId, investigationId, confidence, and shouldRemediate flag. # ai.aient.remediation.investigation\_completed > Generated from `@aient/events`. Do not edit this page by hand. A remediation investigation completed. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.remediation` | | Bounded context | `remediation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `investigations/{investigationId}` | | Schema ID | `sch_01KX2T3SEG75WDY1JV2EE6R8V4` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KX2T3SEG75WDY1JV2EE6R8V4.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.remediation.investigation_completed/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `confidence` | no | `string` | `high`, `medium`, `low` | | `epochId` | no | `string` | - | | `investigationId` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `remediationAttemptId` | no | `string` | - | | `rootExecutionId` | no | `string` | - | | `shouldRemediate` | no | `boolean` | - | | `threadId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "confidence": "high", "epochId": "11111111-1111-4111-8111-111111111111", "investigationId": "investigation_example", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "remediationAttemptId": "11111111-1111-4111-8111-111111111111", "rootExecutionId": "11111111-1111-4111-8111-111111111111", "shouldRemediate": true, "threadId": "11111111-1111-4111-8111-111111111111" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KX2T3SEG75WDY1JV2EE6R8V4.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "investigations/investigation_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.remediation.investigation_completed" } ``` # remediation.pr_created Source: https://docs.aient.ai/webhooks/events/ai.aient.remediation.pr_created CloudEvent fired when Aient opens a remediation pull request, with payload fields problemId, pullRequestUrl, repositoryFullName, and PR status. # ai.aient.remediation.pr\_created > Generated from `@aient/events`. Do not edit this page by hand. A remediation pull request was created. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.remediation` | | Bounded context | `remediation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}/pull-requests` | | Schema ID | `sch_01KXWFVE6YG9M3GQ17VQR2NV03` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KXWFVE6YG9M3GQ17VQR2NV03.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.remediation.pr_created/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `epochId` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `pullRequestUrl` | yes | `string` | - | | `remediationAttemptId` | no | `string` | - | | `repositoryFullName` | no | `string` | - | | `rootExecutionId` | no | `string` | - | | `status` | no | `string` | `pr_open`, `pr_merged` | | `text` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "epochId": "11111111-1111-4111-8111-111111111111", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "pullRequestUrl": "https://github.com/aient/example/pull/1", "remediationAttemptId": "11111111-1111-4111-8111-111111111111", "repositoryFullName": "aient/example", "rootExecutionId": "11111111-1111-4111-8111-111111111111", "status": "pr_open", "text": "example-text" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KXWFVE6YG9M3GQ17VQR2NV03.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example/pull-requests", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.remediation.pr_created" } ``` # remediation.verified Source: https://docs.aient.ai/webhooks/events/ai.aient.remediation.verified CloudEvent fired when an Aient remediation is verified against telemetry, with payload fields problemId, remediationId, and verificationId for closure. # ai.aient.remediation.verified > Generated from `@aient/events`. Do not edit this page by hand. A remediation was verified. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.remediation` | | Bounded context | `remediation` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `problems/{problemId}/verifications/{verificationId}` | | Schema ID | `sch_01M46QK2HDYJBXGESZWB7XAD3T` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01M46QK2HDYJBXGESZWB7XAD3T.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.remediation.verified/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `epochId` | no | `string` | - | | `organisationId` | yes | `string` | - | | `problemId` | yes | `string` | - | | `remediationAttemptId` | no | `string` | - | | `remediationId` | no | `string` | - | | `rootExecutionId` | no | `string` | - | | `verificationId` | no | `string` | - | | `verifiedWindowContext` | no | `object` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "epochId": "11111111-1111-4111-8111-111111111111", "organisationId": "11111111-1111-4111-8111-111111111111", "problemId": "problem_example", "remediationAttemptId": "11111111-1111-4111-8111-111111111111", "remediationId": "remediation_example", "rootExecutionId": "11111111-1111-4111-8111-111111111111", "verificationId": "verification_example", "verifiedWindowContext": { "consequenceTransitionId": "11111111-1111-4111-8111-111111111111", "recurrenceObservedAt": "example-recurrenceObservedAt", "windowEnd": "example-windowEnd", "windowStart": "example-windowStart" } }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01M46QK2HDYJBXGESZWB7XAD3T.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "problems/problem_example/verifications/verification_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.remediation.verified" } ``` # repository.discovered Source: https://docs.aient.ai/webhooks/events/ai.aient.repository.discovered CloudEvent fired when Aient discovers a tenant repository through GitHub, with payload fields organisationId, repositoryFullName, and repositoryId. # ai.aient.repository.discovered > Generated from `@aient/events`. Do not edit this page by hand. A repository was discovered by Aient. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.repositories` | | Bounded context | `repository` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `repositories/{repositoryFullName}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5VA0` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA0.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.repository.discovered/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `organisationId` | yes | `string` | - | | `repositoryFullName` | yes | `string` | - | | `repositoryId` | no | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "organisationId": "11111111-1111-4111-8111-111111111111", "repositoryFullName": "aient/example", "repositoryId": "repository_example" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5VA0.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "repositories/aient/example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.repository.discovered" } ``` # team.invitation_accepted Source: https://docs.aient.ai/webhooks/events/ai.aient.team.invitation_accepted CloudEvent fired when a teammate accepts an Aient team invitation, with payload fields invitationId, memberUserId, and organisationId. # ai.aient.team.invitation\_accepted > Generated from `@aient/events`. Do not edit this page by hand. A team invitation was accepted. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.teams` | | Bounded context | `team` | | Source | `https://aient.ai/api/organisations/{organisationId}` | | Subject | `team/invitations/{invitationId}` | | Schema ID | `sch_01J8X7AKZP4N6QHF2T1Y3R5V93` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V93.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.team.invitation_accepted/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `invitationId` | yes | `string` | - | | `memberUserId` | no | `string` | - | | `organisationId` | yes | `string` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "invitationId": "invitation_example", "memberUserId": "11111111-1111-4111-8111-111111111111", "organisationId": "11111111-1111-4111-8111-111111111111" }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01J8X7AKZP4N6QHF2T1Y3R5V93.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111", "specversion": "1.0", "subject": "team/invitations/invitation_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.team.invitation_accepted" } ``` # telemetry.received Source: https://docs.aient.ai/webhooks/events/ai.aient.telemetry.received Webhook schema for ai.aient.telemetry.received # ai.aient.telemetry.received > Generated from `@aient/events`. Do not edit this page by hand. An environment received usable telemetry in a coalescing window; receivedAt is the bucket start. ## Delivery Contract | Field | Value | | - | - | | Visibility | Tenant-selectable public event | | Topic | `v1.events.telemetry` | | Bounded context | `telemetry` | | Source | `https://aient.ai/api/organisations/{organisationId}/environments/{environmentId}` | | Subject | `environments/{environmentId}` | | Schema ID | `sch_01KREWXD5B2D4ZNKBQCNQSS574` | | Immutable schema | `https://registry.aient.ai/schemas/sch_01KREWXD5B2D4ZNKBQCNQSS574.json` | | Version alias | `https://registry.aient.ai/events/ai.aient.telemetry.received/v1.json` | ## Payload Fields | Field | Required | Type | Enum | | - | - | - | - | | `environmentId` | yes | `string` | - | | `environmentSlug` | yes | `string` | - | | `organisationId` | yes | `string` | - | | `receivedAt` | yes | `string` | - | | `signalKinds` | no | `array` | - | ## Privacy Notes ID-first customer-facing payload. Do not include secrets, tokens, raw telemetry, prompts, logs, or customer content. ## Lifecycle Notes Emitted after the owning bounded-context state transition commits through its Restate handler. ## Compatibility Notes Backward-compatible schema changes may add optional fields; removing fields, changing field types, or removing enum values requires a new schema revision and migration plan. ## Example CloudEvent ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "environmentId": "environment_example", "environmentSlug": "example-environmentSlug", "organisationId": "11111111-1111-4111-8111-111111111111", "receivedAt": "2026-04-25T12:00:00.000Z", "signalKinds": [] }, "datacontenttype": "application/json", "dataschema": "https://registry.aient.ai/schemas/sch_01KREWXD5B2D4ZNKBQCNQSS574.json", "id": "evt_01J8X7AKZP4N6QHF2T1Y3R5VAA", "organisationid": "11111111-1111-4111-8111-111111111111", "source": "https://aient.ai/api/organisations/11111111-1111-4111-8111-111111111111/environments/environment_example", "specversion": "1.0", "subject": "environments/environment_example", "time": "2026-04-25T12:00:00.000Z", "type": "ai.aient.telemetry.received" } ``` # Webhook event schemas Source: https://docs.aient.ai/webhooks/events/index Index of tenant-selectable Aient webhook events covering problem lifecycle, remediation, integrations, onboarding, and repository discovery with schema links. # Webhook event schemas > Generated from `@aient/events`. Do not edit this page by hand. Aient webhooks use CloudEvents 1.0 structured JSON. Only events marked public are tenant-selectable in webhook endpoint settings. ## Tenant-selectable events * [ai.aient.instrumentation.discovery\_started](./ai.aient.instrumentation.discovery_started) - Instrumentation discovery started for an organisation or repository. * [ai.aient.instrumentation.pr\_merged](./ai.aient.instrumentation.pr_merged) - An Aient instrumentation pull request was merged. * [ai.aient.instrumentation.services\_selected](./ai.aient.instrumentation.services_selected) - Instrumentation discovery selected services for a repository. * [ai.aient.integration.github.connected](./ai.aient.integration.github.connected) - A GitHub installation became usable. * [ai.aient.integration.github.disconnected](./ai.aient.integration.github.disconnected) - A GitHub installation became unusable. * [ai.aient.integration.linear.connected](./ai.aient.integration.linear.connected) - A Linear workspace connection became usable. * [ai.aient.integration.linear.disconnected](./ai.aient.integration.linear.disconnected) - A Linear workspace connection became unusable. * [ai.aient.integration.mcp.connected](./ai.aient.integration.mcp.connected) - An MCP client connection became usable. * [ai.aient.integration.scm.connected](./ai.aient.integration.scm.connected) - A source control provider connection became usable. * [ai.aient.integration.scm.disconnected](./ai.aient.integration.scm.disconnected) - A source control provider connection became unusable. * [ai.aient.integration.slack.connected](./ai.aient.integration.slack.connected) - A Slack workspace connection became usable. * [ai.aient.integration.slack.disconnected](./ai.aient.integration.slack.disconnected) - A Slack workspace connection became unusable. * [ai.aient.integration.slack.notification\_channel\_selected](./ai.aient.integration.slack.notification_channel_selected) - A Slack notification channel was selected. * [ai.aient.onboarding.completed](./ai.aient.onboarding.completed) - The tenant completed onboarding. * [ai.aient.organisation.created](./ai.aient.organisation.created) - A tenant organisation was created. * [ai.aient.problem.closed](./ai.aient.problem.closed) - A problem was closed. * [ai.aient.problem.detected](./ai.aient.problem.detected) - A new problem was detected. * [ai.aient.problem.duplicate\_changed](./ai.aient.problem.duplicate_changed) - A problem duplicate relationship changed. * [ai.aient.problem.regressed](./ai.aient.problem.regressed) - A previously closed problem regressed. * [ai.aient.problem.remediation\_decision\_recorded](./ai.aient.problem.remediation_decision_recorded) - A remediation admission decision was recorded for a problem. * [ai.aient.problem.reopened](./ai.aient.problem.reopened) - A previously closed problem was reopened. * [ai.aient.problem.suppression\_changed](./ai.aient.problem.suppression_changed) - A problem alert suppression overlay changed. * [ai.aient.problem.triaged](./ai.aient.problem.triaged) - A detected problem was triaged. * [ai.aient.problem.verification\_started](./ai.aient.problem.verification_started) - A problem entered production verification. * [ai.aient.remediation.investigation\_completed](./ai.aient.remediation.investigation_completed) - A remediation investigation completed. * [ai.aient.remediation.pr\_created](./ai.aient.remediation.pr_created) - A remediation pull request was created. * [ai.aient.remediation.verified](./ai.aient.remediation.verified) - A remediation was verified. * [ai.aient.repository.discovered](./ai.aient.repository.discovered) - A repository was discovered by Aient. * [ai.aient.team.invitation\_accepted](./ai.aient.team.invitation_accepted) - A team invitation was accepted. * [ai.aient.telemetry.received](./ai.aient.telemetry.received) - An environment received usable telemetry in a coalescing window; receivedAt is the bucket start. # Aient webhooks overview Source: https://docs.aient.ai/webhooks/overview Aient delivers tenant-selectable events as CloudEvents 1.0 structured JSON with at-least-once delivery and idempotency through source and id tuples. # Webhooks Aient webhooks deliver tenant-selectable events as CloudEvents 1.0 structured JSON. Each event page includes the immutable schema URL, topic, source template, subject template, payload fields, and an example CloudEvent. ## Delivery semantics | Contract | Behavior | | - | - | | Encoding | CloudEvents 1.0 structured JSON | | Delivery | At least once | | Ordering | Not globally ordered; use event timestamps and IDs | | Idempotency | Use the CloudEvents (`source`, `id`) tuple as the event identity | | Compatibility | Additive schema changes are preferred; removed fields require a new event version | Validate `type`, verify signatures, process idempotently, and return a 2xx response only after durable acceptance. # Webhook signing Source: https://docs.aient.ai/webhooks/signing Verify Aient webhook signatures using the raw request body and endpoint secret, reject stale or malformed signatures, and deduplicate events before processing. # Webhook signing Verify webhook signatures before parsing the event into business logic. Keep the raw request body bytes because signature verification depends on the exact payload that Aient sent. ## Consumer checklist * Read the raw request body before JSON parsing. * Verify the Aient signature header with the endpoint secret configured in Aient. * Reject missing, malformed, or stale signatures. * Deduplicate by the CloudEvents (`source`, `id`) tuple after verification. * Return 2xx only after the event is durably accepted. If verification fails, return a non-2xx response and do not process the event.