The HTTP API
Call the platform from any language. The SDKs are thin clients over these conventions.
Everything the SDKs do is a plain HTTPS call, so any language with an HTTP client can use the platform.
Authenticate
Send your API key (or a user's session token) as a bearer token:
curl https://api.noclick.com/v1/users \
-H "Authorization: Bearer $NOCLICK_API_KEY"Act for a user
Add the NoClick-User header with your id for the user, as it is:
curl -X POST https://api.noclick.com/v1/connections \
-H "Authorization: Bearer $NOCLICK_API_KEY" \
-H "NoClick-User: user_42" \
-H "Content-Type: application/json" \
-d '{"name": "gmail", "integration": "gmail", "reason": "to send your summaries"}'In a path, the id is percent-encoded: GET /v1/users/user_42, GET /v1/users/auth0%7C123.
Integrations and operations
Any key with the tools or credentials:use scope reads the catalog of integrations:
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | Every integration: name, label, kind, subscription, connect_with, credential_types, operation_count and account_free_operation_count; q keeps the ones whose name or label contains it, kind (app or model) the ones of that kind |
GET | /v1/ | One integration, with its operations |
GET | /v1/ | Its operations, each with parameters, read_only, scopes and needs_account; q keeps the ones whose name or description contains it |
GET | /v1/ | One operation, or 404 for a name the integration doesn't have |
POST | /v1/ | Run one operation by its ref (ref, arguments, approval_id), acting for the NoClick-User user when you send one, else for your account |
POST | /v1/ | Run an operation through a connection (arguments, approval_id); ref is the connection's id or name |
account_free_operation_count is how many of an integration's operations need no account, which is 0 for most. An integration whose kind is model is a model provider (anthropic, openai, ...): it has no operations, and a connection to it is an agent's model_connection, never a tool. subscription says its connect link also offers signing in with the provider's plan. POST /v1/connections/{ref}/token refuses a model provider's connection with 403 permission_denied.
Running an operation takes a key with the credentials:use scope. POST /v1/run takes a ref, "name.operation", the same one an agent's tools take, and resolves it where the call acts:
- A connection named
nameruns it, in that connection's account. - Else, when an integration is named
nameand the operation needs no account, it runs on NoClick's key with no connection. - Else the call fails. An operation that needs an account answers
400withinvalid_request: create a connection namedname. A name that is neither a connection nor an integration answers404.
curl -X POST https://api.noclick.com/v1/run \
-H "Authorization: Bearer $NOCLICK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ref": "exa.search", "arguments": {"query": "invoice automation"}}'{"object": "operation_result", "ref": "exa.search", "integration": "exa", "operation": "search", "connection": null, "result": {"results": [...]}}result is the operation's data, what the service answered. When the service refuses the call, the run answers 424 with upstream_error: the service's own words, and its HTTP status as upstream_status. A service that fails or doesn't answer gives 502 with upstream_error. connection names the connection that ran it, or is null on NoClick's key. On a pending connection, an operation with needs_account: false still runs on NoClick's key, and one that needs an account answers 424 with needs_connection. The same rules resolve the refs in an agent's, a toolkit's and a hosted MCP server's tools. POST /v1/toolkits/call with simulated: {world, scenario} (world a world_ id your code makes once per toolkit) resolves the call the same way, then a simulated world answers it instead of the account (Simulate your own tools). A connection's used_by lists the agents, hosted MCP servers and apps (kind agent, mcp_server, project) holding it.
Agents and harnesses
An agent's harness is sdk (the default) or a coding harness: claude_code, codex, opencode, hermes or openclaw. POST /v1/agents and PATCH /v1/agents/{id} take harness, model (the harness's own model; the harness's default when omitted, and after a harness change that sends no model) and model_connection, the name of a model provider's connection in the agent's scope. Every harness but sdk needs one; it may still be pending. They take timezone too (an IANA name, null clears it): every turn opens with the date and time there, else in the acting user's timezone (POST /v1/users and PATCH /v1/users/{id} take one), else UTC.
They take repos as well: up to 10 checkouts, each {"connection", "repository", "branch", "path", "push"} with connection a github connection of the agent's scope (it may be pending), repository its owner/name, path the folder under /workspace (the repository's name when omitted) and push a list of branch globs (omitted: read-only). They're stored as sent and the agent carries them as repos (null for a user reading an account agent). Each is cloned at a conversation's first turn through NoClick's git endpoint, which refuses a push to any ref the globs don't name; a turn whose checkout's connection isn't connected fails with 424 needs_connection (with its connect_url for a caller in the connection's scope). A repo_id entry (a NoClick repo) is in invite-only beta: without access, it answers 400.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | Every harness: name, label, version (null for sdk), default_model, custom_models (whether model also takes ids it doesn't list), model_connection (required or optional), integrations (the model providers that serve it), streaming (tokens or final), shell (built_in or code_capability) and notes. Takes the agents scope |
GET | /v1/ | The models a harness offers, its default first, each a harness_model with id (what model takes) and label. q keeps those whose id or label contains it (any case); limit (50, at most 200) and cursor page it. Takes the agents scope |
A harness's model list comes from its providers' own catalogs, thousands of models on sdk, so it's searched rather than listed with the harness; a search after a few quiet minutes reads them afresh and can take several seconds.
A turn on a coding harness is an agent.message operation like any other, whose step says where it is while it runs: delivering while its sandbox starts and takes the message, then working. It's null on sdk and once the operation ends. A pending model connection fails the turn with 424 needs_connection, a revoked one with 424 credential_disconnected, and a key the provider refuses carries the provider's own words.
{"object": "operation", "id": "3b9e...", "kind": "agent.message", "status": "running", "step": "working", "result": null}A wake-up gives one of an agent's conversations, the NoClick-User header's thread, a turn later; it takes the agents scope. See Wake it up later.
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | 201 with a wakeup: conversation, note (1 to 4,000 characters) and exactly one of at (ISO 8601 with its offset, a minute to a year ahead), after_s (60 to 31,536,000) or when: {"operation_id"}, due when it reaches a terminal status, or {"event", "filters"}, due when the first matching event after it is dispatched (woken_by is then {"event"}); a 101st pending one answers 429 quota_exceeded |
GET | /v1/ | Its wake-ups, newest first; conversation, status (a comma list of pending, delivered, canceled, failed), user_id (all, none or a user's id: whose threads, as on other lists), limit and cursor |
GET | /v1/ | Read one |
POST | /v1/ | Cancel a pending one (409 conflict once it was delivered, canceled or failed) |
A channel's conversation (channel:<id>) takes turns only from the channel, so POST /v1/agents/{id}/messages and POST /v1/agents/{id}/wakeups refuse it with 400 invalid_request (field: "conversation"), and A2A and the AI API refuse it in their own error shapes. A delivered wake-up's operation_id is the agent.message operation its turn runs as, and woken_by says what woke it: {"at"} for a time, {"operation_id", "kind", "status", "result" | "error"} for an operation, {"update"} for a child's update. NoClick's own wake-ups have source update, answer (a connect link the agent asked for is done) or approval (a held call was decided).
Spawning, jobs and asking take the agents scope. See Coordinators and sub-agents.
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | Spawn a child and start its task at once (task, updates, conversation, instructions, tools, budget_usd_per_month, ttl_s, model, harness, model_connection, name): 201 with the child and its job, an agent.task operation. updates lists "blocked", {"before": [patterns]} and {"every_s": n} (60 or more) beside "done"; a tool the parent lacks answers 400 invalid_request (missing), and a 21st live child or a 4th level 429 quota_exceeded |
GET | /v1/ | The jobs it started, newest first: its children's tasks (agent.task) and the messages it sent with its agents tools (agent.message) (status, a comma list or live; kind; agent_id, the agent doing the work; conversation, its own whose turn each wakes; limit, cursor). A user reads its own; your account every user's |
GET | /v1/ | A job's updates, oldest first (limit, cursor); 404 for an operation that isn't a job |
POST | /v1/ | Ask it something without stopping it (question, conversation, interject): 202 with an agent.ask operation, whose result is an agent reply plus joined (the turn an interjected question joined). With interject and nothing running, 202 with the agent.message of an ordinary turn. An agent on a Claude or ChatGPT plan answers a snapshot ask with 409 not_available (reason: "plan_snapshot") |
Each agent carries job_id, the task spawn started it with. A job's view adds started_by (its parent, or the agent that sent the message), conversation (the worker's: a child's is task), parent_conversation (the one its updates wake, or null), updates and latest_update.
In tools, "agents" gives an agent the tools to spawn, message, ask, check, list and cancel other agents in its own conversations, and "agents.spawn" and its siblings give one each; any other agents.<name> answers 400, and a connection can't be named agents. A message it sends is an agent.message job, so its reply is the job's done update. A job_update has id, job_id, agent_id, kind (done, blocked, before, every or message), text, tool, arguments, status (the job's then), result, error, waiting_on ({"kind": "connection" | "approval", "url", "operation_id"}), wakeup_id and created_at.
A test run takes the agents scope too:
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | A real turn against a simulated world (message, and scenario, up to 8,000 characters): 202 with an agent.rehearse operation whose result is reply, tool_calls (tool, arguments, result), transcript, scenario and cost_usd. An 11th queued or running run of the account answers 429 quota_exceeded; a model connection that can't run (pending, disconnected, or not for its model) is refused up front with the error its turn would fail with |
An agent's memory takes the agents scope. Each call acts on one layer: for an account agent the account's (no NoClick-User) or the acting user's; a user's own agent has only its user's. user_id (none or a user's id, from a key acting for the whole account) names the layer instead of the header, without acting for the user, so a suspended user's memory still reads.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | The layer's entries by name, without content. With q: the entries of the layer and (a user's layer of an account agent) the account's holding any of its words, or its text in a name or description, best first, with content (limit, 10, at most 100) |
GET | /v1/ | One memory_entry: name, description, content, version, updated_by (agent, api or console), user_id (null for the account's), created_at, updated_at |
PUT | /v1/ | Save it (content, 1 to 20,000 characters; description, up to 200, required for a new entry and kept when omitted; version, only over that version, else 409 conflict with current_version). A name is 1 to 64 of a-z 0-9 . _ -; a 501st entry in the layer answers 429 quota_exceeded |
DELETE | /v1/ | Forget it (version as on PUT); 404 when there's none |
Streaming, watching and sharing take the agents scope. See Sharing and streaming.
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | The turn as server-sent events instead of its operation: text (text), tool_call (tool, arguments, held), tool_result (tool, result, is_error), then done (the turn's result: text, conversation, tool_calls, cost_usd, model_usage) or error ({"error": {...}}), each with operation_id. A refusal before the turn starts (a busy conversation, an expired agent) is an ordinary error response |
GET | /v1/ | One conversation live, from now until it's deleted: message (role, text, operation_id: the turn that sent it, null for none), text, tool_call, tool_result and status (awaiting_human or active). user_id (none or a user's id) names whose thread, as on the conversation read |
GET | /v1/ | Its public chat page, a share_link: url, enabled, agent_id, created_at (first shared) and rotated_at; 404 if it was never shared |
POST | /v1/ | Turn the page on, made the first time, at the URL it has |
PATCH | /v1/ | {"enabled": false} turns it off (true back on) |
POST | /v1/ | A new URL; the old one stops at once. Whether it's on stays as it was |
POST | /v1/ | A visitor's turn, as on the page: text, visitor (16 to 40 of A-Z a-z 0-9 - _, the page's random id for the visitor) and conversation (default main). Waits up to 25 seconds and answers a share_reply: operation_id, conversation, status (running, succeeded or failed), text, and error when it failed; still running answers 202. Takes a publishable key that lists the agent, or the owner's key |
GET | /v1/ | That turn's share_reply, waiting up to wait_s (at most 25) for it to finish: only a turn the same key started on this page |
Both streams send a : keepalive comment every 15 seconds. A turn's result carries model_usage (input_tokens, output_tokens, cached_tokens, usd) for an agent with a model_connection, else null; so does each conversation in GET /v1/agents/{id}/conversations. Only the agent's owner shares it: a user acting on an account agent gets 403.
Each agent carries ttl_s (the time to live it was made with), status (working while a turn is queued, running or held, idle, or expired past its ttl_s), last_active_at, running_turns and child_count; with NoClick-User set, an account agent's last four are null, and its activity filters never match it. GET /v1/agents lists expired agents for the 30 days they're kept, filters by status, active_since, parent_id, temporary (true or false) and harness, and sorts by order (created, the default, or last_active). An expired agent answers a message or a PATCH with 410 expired; DELETE removes it.
Triggers and channel subscriptions
An agent's triggers and its pause switch take the agents scope, a channel's subscriptions and schedules the channels scope, and the events catalog tools or credentials:use. See Triggers and Subscribe a channel to events.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | The app events an integration pushes, each an integration_event: name, description, filters (field to what it matches) and required_filters |
GET | /v1/ | Its schedules, webhook, app-event and NoClick-event triggers, then the inboxes (email) and numbers (call) that wake it; a user's call on an account agent is 403 |
POST | /v1/ | 201 with an agent_trigger. The body is {"kind": "webhook", "name", "message", "conversation"} or {"kind": "event", "connection", "event", "filters", "message", "conversation"} or {"kind": "platform", "type", "filters", "message", "conversation"} (a NoClick event: a type or * pattern, filters dotted paths into its data, conversation a template over the event) or {"kind": "cron", "cron", "tz", "message"} (five fields; tz an IANA name, UTC by default, or "user" on a user's agent) or {"kind": "queue", "queue", "batch_size", "concurrency", "message", "conversation"} (a queue of the agent's own scope by name; batch_size 1 to 25 messages a turn, concurrency 1 to 50 turns at once, conversation only with a batch_size of 1). A taken webhook name is 409 conflict, a 21st trigger 429 quota_exceeded |
DELETE | /v1/ | Remove one; an email or call one is 400, changed on its inbox or number |
POST | /v1/, /resume | Stop every trigger waking it (schedules skipped, webhooks 409, events, mail and calls ignored), or start them again, a trigger its failures stopped included; the agent, with paused |
GET | /v1/ | Its schedules, each an agent_trigger with channel_id |
POST | /v1/ | 201: {"kind": "cron", "cron", "tz", "message"}, message required (it's posted in the room as the account); 20 per channel |
DELETE | /v1/ | Remove a schedule |
POST | /v1/ | 201 with a subscription: source (an event, webhook or platform definition, without conversation), wake (null, "all" or member agent ids) and, for a webhook or a NoClick event, filters (dotted paths into its JSON body, or the event's data) |
GET | /v1/ | Its subscriptions |
GET | /v1/ | Read one: its source, who it wakes, its status |
DELETE | /v1/ | Stop one; a webhook's URL then answers 410 gone |
POST /v1/agents takes triggers, a list of the same definitions, checked before anything is made, and POST /v1/channels takes a channel's schedules the same way; PATCH /v1/channels/{id} changes an open channel's mode, max_agent_turns, max_mention_hops and budget_usd (null lifts the budget). An agent_trigger has id, agent_id, channel_id, kind, spec (its definition without kind), status (active, paused while its agent is, or error, with error saying why it hears nothing), url (a webhook's), next_run (a schedule's), last_fired_at and created_at. An agent has paused and trigger_kinds (null for a user reading an account agent) and timezone; a subscription has channel_id, source, wake, filters, status, error, url and last_event_at.
A POST to a webhook's url answers 202 with {"object": "trigger_delivery", "status", "operation_id", "conversation"} for an agent (status is pending while its conversation answers an earlier delivery, failed with an error when the account can't pay), and {"status": "posted", "message_id"} for a channel (200 filtered when its filters don't match). A NoClick-Conversation header picks an agent's conversation. The turn is an agent.message operation whose input has the event and the trigger (id, kind, title). A message a subscription posts has author.type app, its subscription_id and the event as it arrived.
Webhooks and events
These take the webhooks scope, and reading a secret webhooks:reveal, which * doesn't grant. An endpoint lives where the call acts: your account's receives every event, a user's (NoClick-User) only that user's. See Webhooks and events.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List endpoints, newest first (url finds one; user_id) |
POST | /v1/ | 201 with a webhook (url: https and public, events: types or * patterns, description); its secret is in this response only. A URL the scope already has is 409 with existing_id, a 17th endpoint 429 |
GET | /v1/ | Read one: events, enabled, status (healthy, failing or disabled), success_rate_24h and last_failure |
PATCH | /v1/ | Change url, events or description (null clears it), or pause it (enabled: false) |
DELETE | /v1/ | Delete it; its retries stop |
GET | /v1/ | {"object": "webhook_secret", "secret", "previous_secret_expires_at"} (webhooks:reveal), recorded in the request log with its key |
POST | /v1/ | The webhook with its new secret; the old one also signs for grace_s (default 86,400, at most 7 days, 0 stops it at once) |
GET | /v1/ | Its deliveries, newest first (status, event_id) |
POST | /v1/ | Send one again now: the delivery, with this attempt |
POST | /v1/ | Send a sample of event_type now and wait up to 10 seconds: an unrecorded delivery |
GET | /v1/ | Events, newest first, from the last 30 days (type: types or * patterns, comma-separated; since, inclusive; until; user_id) |
GET | /v1/ | One event with its deliveries |
GET | /v1/ | The catalog: name, description and emitted (whether NoClick sends it today) |
An event has id, type, user_id, created_at and data. A webhook_delivery has id, webhook_id, event_id, event_type, status (pending, retrying, succeeded or failed), attempts, response_status, response_body (its first 4 KB), request_body, duration_ms, error, next_retry_at and created_at. A delivery is a POST of the event's JSON with a NoClick-Signature: t=<unix seconds>,v1=<hex> header, where the hex is HMAC-SHA256 of <t>.<body> keyed with the endpoint's secret (two v1 values while a rotated secret's grace runs). A 2xx within 10 seconds succeeds; anything else, a redirect included, is retried after a minute, five minutes, half an hour, two hours, five hours, then every 12 hours, for 3 days.
Workspaces
Workspaces take the channels scope and live where the call acts: your account, or the user NoClick-User names. See Workspaces.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List workspaces, newest first (name, status: active or archived, user_id) |
POST | /v1/ | 201 with a workspace (name, members, budget_usd_per_month, logo_url: an https URL). A name an active workspace of the scope has is 409 with existing_id; a 101st active one 429 |
GET | /v1/ | Read one, with its members |
PATCH | /v1/ | Change its name, logo_url or budget_usd_per_month (null clears the last two) |
DELETE | /v1/ | Delete it with its channels and their messages |
POST | /v1/ | Close its channels and stop its subscriptions; everything stays readable and it takes no more changes (409) |
POST | /v1/ | 201 with a workspace_member: an agent of its scope ({"agent_id"}) or one of your users ({"user_id"}; a user's own workspace takes only that user) |
DELETE | /v1/ | Remove one, from every one of its channels too |
POST | /v1/ | 201 with a subscription: source and filters as a channel's take them, channels ("all", its open channels now and later, or a list of its open channels' ids) and wake (null, "all" or the workspace's agent ids) |
GET | /v1/ | Its subscriptions |
GET | /v1/ | Read one |
DELETE | /v1/ | Stop one; a webhook's URL then answers 410 gone |
A workspace has id, name, logo_url, user_id, status, budget_usd_per_month, spent_usd (this UTC month), channel_count (its open channels), members and created_at; a workspace_member has id, type (agent or user), agent_id, user_id and name. POST /v1/channels takes workspace_id (its agent and user members must be the workspace's, 400 otherwise, and its name unique among the workspace's open channels, 409 with existing_id); a workspace channel's members and a post's as may be {"user_id": "<your id>"}; GET /v1/channels filters by workspace_id and name; a channel has workspace_id. A workspace subscription's webhook answers {"status": "posted", "message_ids"}, one message per open channel it posts in.
Volumes and buckets
Volumes take the volumes scope and buckets storage. Each lives where the call acts: your account, or the user NoClick-User names. A user reaches its own volumes and your account's, and its own buckets. See Volumes and Storage.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List volumes, newest first (name; user_id: by default the caller's user's and your account's) |
POST | /v1/ | 201 with a volume (name). A taken name is 409 with existing_id; region answers 409 not_available (volumes aren't placed by region yet) |
GET | /v1/ | Read one: size_bytes, file_count (as last measured, measured_at) and mounted_by |
DELETE | /v1/ | Delete it and its files; 409 while an agent or a running sandbox mounts it |
GET | /v1/ | The file's bytes (up to 100 MB), or with view=list a folder's entries in path order (recursive, limit up to 1,000, cursor: the last path seen) |
PUT | /v1/ | Store the request body at path (up to 100 MB), making folders as needed |
DELETE | /v1/ | Remove a file, or a folder (recursive=true when it isn't empty, else 409) |
POST | /v1/ | {src, dst}: dst becomes a copy of the file or folder |
GET | /v1/ | List buckets, newest first (name, user_id) |
POST | /v1/ | 201 with a bucket (name, public, region: a location hint, fixed). A taken name is 409 with existing_id |
GET | /v1/ | Read one: size_bytes, object_count (as last measured), public_base_url, used_by |
PATCH | /v1/ | {public}: open it, or close it and stop every public URL at once |
DELETE | /v1/ | Delete it; 409 while it holds files (unless force=true) or an agent has it in its tools |
GET | /v1/ | List files in key order (prefix, delimiter, limit up to 1,000, cursor), or with key read one's bytes (view=metadata for its details) |
PUT | /v1/ | Store the request body at key, up to 100 MB (content_type, metadata: a JSON object of strings, up to 2 KB) |
DELETE | /v1/ | Remove a file |
POST | /v1/ | {key, method, expires_in, content_type}: a link that reads (GET) or uploads (PUT, up to 5 GB) one key, 60 seconds to 7 days |
A volume_entry has path (from the volume's root), type (file or directory), size and updated_at; a bucket_object has key, size, content_type, etag, metadata, updated_at and folder. A public bucket serves each file at public_base_url + key, which redirects to a short-lived signed link. POST /v1/sandboxes, POST /v1/agents and PATCH /v1/agents/{id} take volumes ({"/data": "<volume id or name>"}), and both carry it. A bucket is one of an agent's tools as bucket:<id> (all five operations) or bucket:<id>.<operation> (objects, head, put, signed_url, remove), bound as {"tool": "bucket:<id>.put", "bind": {"prefix": "users/{{user.id}}/"}}.
An agent's per-conversation files take the agents scope, in the caller's user's thread (else the agent's own user's, else your account's):
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | The workspace's files, each with a download url good for an hour |
GET | /v1/ | Read a file |
PUT | /v1/ | Write a file (up to 100 MB) |
GET | /v1/ | A link to a file (expires_in_s, 60 to 86,400) |
Secrets, dicts and queues
Each takes its own scope (secrets, dicts, queues) and lives where the call acts: your account, or the user NoClick-User names, its name (1 to 64 letters, digits and . _ -) unique there; a taken name is 409 with existing_id. A user's call reads its own dicts and queues, and its own secrets and your account's. Dicts and queues keep their data in us-east (region takes no other yet: another, or a default region elsewhere, is 400 naming the regions that do). See Secrets, Dicts and Queues.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List secrets, with their key names (name; user_id: by default the caller's user's and your account's) |
POST | /v1/ | 201 with a secret: name, values (up to 100 keys, each an environment variable name, not PATH, HOME or one starting LD_, NC_ or NOCLICK_; each value up to 32,768 characters) |
GET | /v1/ | Read one: its key names and who uses it (used_by), never a value |
PATCH | /v1/ | {values, replace}: merged into what's there (a null removes a key), or with replace: true in place of it; the same values write nothing |
DELETE | /v1/ | Delete it; 409 while an agent lists it |
GET | /v1/ | List dicts (name, user_id) |
POST | /v1/ | 201 with a dict: name, region |
GET | /v1/ | Read one: key_count, size_bytes |
DELETE | /v1/ | Delete it and its keys |
GET | /v1/ | Keys in byte order with their values and expiry (prefix, limit up to 1,000, cursor), or only the keys with view=keys |
POST | /v1/ | {items: [{key, value, ttl_s}]}: up to 1,000 keys, all or none |
GET | /v1/ | One key (percent-encoded, so any string, / too); missing or expired is 404 with key |
PUT | /v1/ | {value, ttl_s, if_absent}: set it (no ttl_s clears an expiry); if_absent only where it's missing or expired, atomically, answering written |
DELETE | /v1/ | Remove it, answering the key as it was; missing is 404 with key |
POST | /v1/ | {by, ttl_s}: add to an integer atomically (a missing key starts at 0, ttl_s set only then); anything but an integer is 400 |
POST | /v1/ | Remove every key, answering how many |
GET | /v1/ | List queues: depth, in_flight and dead_letter_count for each |
POST | /v1/ | 201 with a queue: name, max_attempts (1 to 100, 5 by default), region |
GET | /v1/ | Read one, with used_by (the agents an OnQueue trigger hands its messages to) |
PATCH | /v1/ | {max_attempts}; messages already on it follow it |
DELETE | /v1/ | Delete it and its messages; 409 while a live agent's OnQueue trigger reads it |
POST | /v1/ | {messages: [{body, delay_s, dedupe_key}]}: up to 100, all or none (body up to 256 KB of JSON, delay_s up to 7 days, a dedupe_key repeated within 24 hours adds nothing and answers the first message's id) |
GET | /v1/ | Take up to limit (10) messages, each leased for lease_s (1 second to 12 hours) and carrying its lease_id, waiting up to wait_s (60) for one; view=peek reads the oldest waiting and in flight instead |
GET | /v1/ | Read one message |
POST | /v1/ | {lease_id}: done, deleted (204); once the lease ran out, 409 |
POST | /v1/ | {lease_id, delay_s, error}: a failed attempt, back after delay_s, or a dead letter at max_attempts |
POST | /v1/ | {lease_id, delay_s}: back without spending an attempt |
GET | /v1/ | Dead letters, newest first, with their last_error |
POST | /v1/ | Back on the queue, its attempts reset |
DELETE | /v1/ | Drop a dead letter |
GET | /v1/ | depth and in_flight every 15 minutes over the last hours (up to 168) |
POST | /v1/ | Drop waiting messages and dead letters (in flight ones finish), answering how many |
A queue_message has id, queue_id, body, status (waiting, delayed, in_flight or dead), attempts (deliveries, this one included), enqueued_at, visible_at (while delayed), lease_expires_at, last_error, dead_lettered_at, and on a take its lease_id. Waiting messages and dead letters are kept 14 days. POST /v1/sandboxes, POST /v1/agents and PATCH /v1/agents/{id} take secret_ids (their keys in the environment, a later one's winning; an agent's never a model provider's key), and both carry it. POST /v1/dicts/{id}/export and POST /v1/queues/{id}/export start a dict.export or queue.export operation (see Export).
Regions
Where a resource runs or keeps its data, in NoClick's own names. See Regions.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | Every region: name (us-east, us-west, eu, ap), label, location, kinds (what it places: sandbox, agent, bucket, database, project and machine everywhere, browser but in us-west, and in us-east dict and queue) and default (your account's default). kind keeps those placing it; another kind answers 400. Takes the sandboxes, agents, storage, databases, apps, machines, browsers, dicts, queues or volumes scope |
POST /v1/sandboxes and POST /v1/agents take region, and a sandbox and an agent carry it (null: wherever NoClick had capacity). A missing region takes a child agent's parent's, else your account's default. An agent's region never changes: PATCH /v1/agents/{id} with another answers 409 conflict with region. Your account's default is set in the console.
Hosted MCP servers
A hosted MCP server serves a toolkit's tools at one URL; these take the mcp_servers scope and live where the call acts: your account, or the user NoClick-User names, whose connections its calls act in. See Hosted MCP servers.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List servers, newest first (name, user_id) |
POST | /v1/ | 201 with an mcp_server (name, tools: what a toolkit takes, at most 64 tools). A name the scope has is 409 with existing_id; a 101st server of the account 429 |
GET | /v1/ | Read one: its url, tools and tool_count |
PATCH | /v1/ | Change its tools or name; the URL stays |
DELETE | /v1/ | Delete it; its URL stops |
POST | /v1/ | A new URL; the old one stops at once |
GET | /v1/ | The MCP clients that have connected, most recently seen first |
An mcp_server has id, name, url, tools (as sent), tool_count (what it serves right now: an entry whose connection isn't connected serves nothing), user_id, created_at and last_client_at. An mcp_client has name and version as the client gave them, first_seen_at, last_seen_at and tool_count (what it saw when it last listed). The url speaks MCP over Streamable HTTP (stateless, JSON replies) and needs no key: initialize answers with an Mcp-Session-Id the client sends back, tools/list serves the tools, and tools/call runs one it serves as a toolkit call, as the key that made the server, recorded as a tool call with source mcp_server and its mcp_server_id. GET /v1/tool_calls?source=mcp_server lists them.
Inboxes, phone numbers and brand
These take the inbox, phone and brand scopes. See Inboxes, Phone numbers and Brand.
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | 201 with an inbox (local_part, name, agent_id, senders: anyone, users or owner) |
PATCH | /v1/ | Change its agent_id (null: none) or senders, from the next message |
GET | /v1/ | Received mail, oldest first: after a message id, since a time (with a timezone), wait_s to long-poll. Each message has from (its From address when a DKIM signature aligned with it verified, else the envelope sender), from_source (header or envelope), envelope_from, authenticated, conversation (its email thread's, inbox:<inbox id>:<thread id>), agent_operation_id (the turn it woke) and agent_error (why it woke none, or why that turn failed) |
POST | /v1/ | An inbox.wait_for operation for the first matching message (sender, subject_contains, timeout_s); with since, mail already received at or after that time matches too |
GET | /v1/ | Numbers for sale (country, type: local, mobile, toll_free, national or shared_cost; area_code, locality, region, contains, starts_with, ends_with, features: voice or voice_sms), each with monthly_usd, upfront_usd, capabilities, requirements (needs: none, address, documents or steps, and items, each with its id, kind, name, description, example and values) and texting (registration, unavailable or none) |
GET | /v1/ | The countries numbers are in stock in, each with its types, texting_types, documents_types (the kinds bought with documents the carrier reviews first) and steps_types (the kinds that ask a step that can't be taken here, which can't be bought here yet); right after a deploy, while that's still being checked, 503 not_available with Retry-After |
GET | /v1/ | Where in a country numbers of a type are for sale: cities, or area codes in the US and Canada (kind, value, region) |
POST | /v1/ | Buy one (number with its country and type, or country, type and area_code or locality for the first one there; max_monthly_usd, max_upfront_usd, address where a requirement's kind is address or contact, requirements where its country asks for documents (each textual and document requirement's id to its text or an uploaded document's id), name, record_calls): 201 with the number, or 202 with a phone_number_order while the carrier reviews the documents, charged nothing until it completes; a number whose requirements.needs is steps answers 409 not_available |
POST | /v1/ | 201: upload a document for one requirement (country, type, requirement_id, filename, file: a PDF, PNG or JPEG up to 20 MB, base64); the carrier holds it and you name its id in a purchase's requirements within 25 minutes (one a purchase named can be named again) |
GET | /v1/ | A number order in the carrier's review: status in_review, rejected (reason, the carrier's words, and deadline), completed (phone_number_id: the number, now charged) or failed (nothing charged), and while it's open the requirements a resubmission answers |
POST | /v1/ | File corrected answers (address, requirements) for an order the carrier sent back; it's in_review again. Any other order answers 409 |
PATCH | /v1/ | Rename it (name) or record its calls (record_calls), from the next call |
POST | /v1/ | Route its calls to an agent (agent_id) and set afresh how they run: transfer_to (E.164: the agent's transfer_call) and callers (anyone, users or owner). texts answers 409 not_available |
GET | /v1/ | The calls it answered and placed, newest first (direction, since, limit, cursor) |
GET | /v1/ | The numbers in your phone provider account (connection, by name or id), each with label, routed_elsewhere, and phone_number_id and agent_id when it's already connected to NoClick |
POST | /v1/ | 201: connect one you keep with your provider (number, connection, name, record_calls); its calls stay where they go until it's answered |
POST | /v1/ | Hand a connected number back to its provider account, its calls going where they went before |
A number has source (bought or connected), country, type, connection (a connected number's), agent_id, transfer_to, callers, record_calls and monthly_usd (null for a connected number, which its provider bills). A call has direction, from, to, status (calling, then completed, not_connected, refused, failed or lost), user_id (the user it ran as), transcript, transfer (to, status, seconds) and recording_url (a signed link valid for an hour, when the number records its calls).
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | The account's brand (is_default: true while none is set) |
PUT | /v1/ | Replace it: name, logo_url, color, support_url, email_from_name and oauth_apps ({provider: secret id}, an account secret holding CLIENT_ID and CLIENT_SECRET); a field left out is cleared |
PATCH | /v1/ | Change only what's sent (null clears a field); oauth_apps changes the providers it names (null for one removes it), oauth_apps: null removes them all |
DELETE | /v1/ | Back to NoClick's defaults |
oauth_redirect_urls has, for every provider you can bring an OAuth app for, the redirect URI to register with it. An account key changes the brand; a user's key only reads it.
AI models
/v1/ is OpenAI-compatible: point an OpenAI client at https://api.noclick.com/v1/ai with your key. A model call takes the ai scope; model: "agent:<name>" (or agent:<id>) talks to one of your agents and takes agents. See AI models.
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | OpenAI Chat Completions: model (an openrouter/ id, agent:<name>, or omitted for the default), messages, stream, tools, tool_choice, response_format, max_tokens, temperature. An agent's conversation rides metadata.conversation |
POST | /v1/ | OpenAI Responses: input (a string or items), instructions, function tools, text.format, stream, previous_response_id; for an agent, conversation |
POST | /v1/ | OpenAI Embeddings for 1 to 2,048 texts (dimensions) |
GET | /v1/ | The models and your agents in OpenAI's list shape, each with kind and its price in US dollars (input_usd_per_token, output_usd_per_token, usd_per_minute, usd_per_minute_with_speakers); kind is chat, embedding, image, transcription or agent |
POST | /v1/ | One image: prompt, model, size (WIDTHxHEIGHT) -> {url, expires_at, width, height, cost_usd} |
POST | /v1/ | JSON {url, model?, language?, speakers?, background?}, or the audio's bytes with the rest in the query (speakers=true or names comma-separated) -> {text, language, duration_s, segments, cost_usd}. speakers (each segment's speaker) and background (202 with an ai.transcription operation whose result is the transcript) run on noclick/transcribe-1, which is in invite-only beta: without access it answers 409 not_available |
Every answer carries cost_usd. Errors on these routes use OpenAI's shape, {"error": {"message", "type", "param", "code"}}, with NoClick's type in code and next_step beside it. A request that doesn't stream must answer within 90 seconds or fails with 504 request_timeout (sent with x-should-retry: false); a stream starts at once and sends a : keepalive comment every 15 seconds while it waits.
Workflows
Workflows, built from a description by NoClick's builder and run from your code; they take the workflows scope. See Workflows.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | Your account's workflows, most recently changed first: q (a prefix of the name), name (exactly) |
POST | /v1/ | Create one: {name, instructions?, description?} -> 201; with instructions its build is a workflow.build operation |
GET | /v1/ | Read one: its steps, inputs (a JSON schema of what its trigger takes) and app_url |
DELETE | /v1/ | Move it to the trash (restorable in the app for 30 days) |
POST | /v1/ | Change it from a description: {instructions} -> 202 with a workflow.build operation, whose result is {summary, steps}; 409 while another build runs |
POST | /v1/ | Answer what a build asks (its operation's question): {values, message?, skip?} |
POST | /v1/ | Run it: {inputs} -> 202 with a workflow.run operation, whose result is the run (outputs by step name) |
GET | /v1/ | Its runs, newest first, however they started: status (running, succeeded, failed), user_id |
PUT | /v1/ | Turn its HTTP endpoint on: {public?} -> {url, method, public} |
POST | /v1/ | The endpoint: the inputs as the body -> the run's outputs, or 202 with its operation after 30 seconds |
GET | /v1/ | A run the endpoint answered 202 for, answered the same way once it's done |
A public endpoint also takes a publishable key that lists it.
Objects and lists
Objects are JSON with an object type and an id:
{"object": "user", "id": "user_42", "name": "Ana Lima", "status": "active"}Lists wrap them with a cursor. Pass next_cursor back as cursor for the next page; null means you've reached the end:
{"object": "list", "data": [...], "next_cursor": "eyJ0IjoiMjAyNi0..."}Errors
A failed request answers with its HTTP status and an error object:
{
"error": {
"type": "needs_connection",
"message": "The 'gmail' connection has no Gmail account connected for this user yet.",
"next_step": "Send the user connect_url; retry once the connect operation succeeds.",
"connection": "gmail",
"integration": "gmail",
"connect_url": "https://noclick.com/credential/provide/...",
"operation_id": "3b9e..."
}
}type is stable; the Error reference lists each one with its status. A path no route serves answers 404 not_found, and a method its route doesn't take 405 invalid_request (with Allow). When a key's request rate is over its limit, the 429 carries Retry-After; a daily send cap says when it frees in retry_after instead.
Every response carries an X-Request-Id. Quote it when you contact support.
Make writes safe to retry
Send an Idempotency-Key header with every POST. Repeating a request with the same key and the same body returns the first response instead of running again, for 24 hours. A replayed response carries Idempotent-Replayed: true. A request refused with a 4xx isn't remembered, so a repeat runs again, and a repeat that arrives while the first request is still running gets a 409: send it again shortly.
Long-running work
Work that waits returns an operation object:
{"object": "operation", "id": "3b9e...", "kind": "credential.connect", "status": "awaiting_human", "result": null}Long-poll it until it's final. Each call waits up to the timeout_s you pass (up to about a minute) and returns the operation as it stands:
curl "https://api.noclick.com/v1/operations/3b9e.../wait?timeout_s=50" \
-H "Authorization: Bearer $NOCLICK_API_KEY"GET /v1/operations/{id} reads it without waiting, and POST /v1/operations/{id}/cancel cancels it.
From a browser
The API answers cross-origin requests from any origin. Use a session token or a publishable key, never your secret key, and send it as a bearer; the API never reads cookies.
Keys and the CLI
A publishable key (nk_pub_) is made on the console's API keys page (POST /v1/keys with type: "publishable", public_endpoints as {kind, id}, per_visitor_limit_per_minute and allowed_origins; GET /v1/public_endpoints lists what may go in it, and PATCH /v1/keys/{id} changes the three): it calls only the public endpoints it lists, such as a shared agent's chat, from those origins, within its limit per visitor, and every other route answers it 403. A key's view says its type (secret or publishable) and shows a publishable key in full, as publishable_key. The CLI signs in with routes of its own; the first two need no key:
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | Start a sign-in: a device code, and the link and short code a person approves in the console. Limited per address |
POST | /v1/ | Poll with the device code: pending with the interval_s to wait (slow_down, with a longer one, when polled sooner), then approved with the new key, once; 403 when denied, 410 once the code expired or handed its key over |
GET | /v1/ | Signed in as the account owner: the sign-in waiting for this code, with its machine and the address it started from |
POST | /v1/ | Signed in as the account owner: {"approved": true} lets the next poll take a key named after the machine; false denies it |
DELETE | /v1/ | Revoke the secret key that makes the call (noclick logout) |
Databases, repos, apps, domains and browsers
These follow every convention above. A resource lives at /v1/ and /v1/; GET /v1/<plural>?name= finds one by name, which is unique for its user; user_id on a list reads across users (all, none, or one user's id), while the NoClick-User header acts as one. Actions are POST to /{id}/, and every write takes an Idempotency-Key.
Long work answers 202 with an operation of its own kind (project.deploy, domain.buy, database.restore, workflow.run, user.export, ...), which you wait on as above; /wait returns its result once it has succeeded. A purchase takes a ceiling (max_price_usd, max_monthly_usd) and answers 402 price_exceeded above it, a call your account can't pay for answers 402 payment_required, and a purchase your policy holds waits in awaiting_human until a person approves it. Every purchase is charged to your account; NoClick never bills your users. Secret values are write-only, and tokens and credentials come back short-lived.
Databases
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List databases (name, engine) |
POST | /v1/ | Create one: name, engine (sqlite by default, or postgres) |
GET | /v1/ | Read one: engine, status, size, table count |
DELETE | /v1/ | Delete it and its data (409 while it has branches) |
POST | /v1/ | Run one statement (sql, params, read_only), or several in one transaction (statements, each {sql, params}) |
GET | /v1/ | The migrations applied, in order |
POST | /v1/ | Apply the migrations not applied yet |
GET | /v1/ | Its tables, their columns and row counts |
POST | /v1/ | Restore it to a moment (at): a database.restore operation |
GET | /v1/ | The databases branched from it |
POST | /v1/ | Branch a copy (name, at): a database.branch operation; into Postgres (engine) is in invite-only beta |
POST | /v1/ | A short-lived connection URL (Postgres only) |
POST | /v1/ | Export it as SQL: a database.export operation |
In an agent's tools, a database is database:<id> (query, execute and schema), or database:<id>.query for one operation.
Repos
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List repos |
POST | /v1/ | Create one |
GET | /v1/ | Read one: default branch, size, last commit |
DELETE | /v1/ | Delete it |
POST | /v1/ | A git URL with a short-lived token inside (write, ttl_s) for plain git over HTTPS |
GET | /v1/ | Its commits, newest first (branch) |
POST | /v1/ | Commit files in one commit (content, or null to delete) |
GET | /v1/ | List a folder (prefix, ref, recursive), or read a file's bytes (path) |
GET | /v1/ | Its branches and their heads |
POST | /v1/ | A git bundle to take with you, or with {to: "github", connection, owner, name?, private?} a copy in a new GitHub repository through a GitHub connection: a repo.export or repo.export_to_github operation |
Apps
An app is a project: a repo deployed to a URL, with what the running app needs.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List apps |
POST | /v1/ | Create one from a repo (runtime edge or container; framework detected unless given; budget_usd_per_month, 10 when omitted; deploy_on_push) |
GET | /v1/ | Read one: URL, repo, production branch and deployment, domains |
PATCH | /v1/ | Change its repo, framework, production branch, budget_usd_per_month (null removes it), deploy_on_push (off, production, all) or check_paths (what a promote checks first) |
DELETE | /v1/ | Move it to the trash, offline within seconds and restorable for 30 days (delete_data also moves its repo and the databases and buckets bound to it) |
GET | /v1/ | Its deployments, newest first (branch) |
POST | /v1/ | Build and deploy a branch (branch, commit): a project.deploy operation |
GET | /v1/ | Read a deployment: branch, commit, status, URL, started_by (deploy or push) |
GET | /v1/ | Its build output or what it printed while serving (kind) |
GET | /v1/ | How its requests went (since): by status, the error rate, the slowest routes |
POST | /v1/ | Load paths of it in a browser: a deployment.check operation giving each one's status, load time, console errors and screenshot |
POST | /v1/ | Stop a build |
POST | /v1/ | Put it live (a rollback promotes an earlier one): a deployment.promote operation, checked first when the app has check_paths (force skips it) and undoable with its data |
GET | /v1/ | What it reads by name: databases, buckets, dicts, queues, secrets, connections and agents |
POST | /v1/ | Bind a resource under a name |
DELETE | /v1/ | Unbind it |
GET | /v1/ | Its secrets' names and when each was set (never the values) |
PUT | /v1/ | Set a secret |
DELETE | /v1/ | Delete a secret |
GET | /v1/ | Its schedules |
POST | /v1/ | Call a path on a cron schedule (cron, tz, path, name); the same name again redefines it |
GET | /v1/ | Read a schedule: next and last run, last_status and last_error, status |
DELETE | /v1/ | Delete it |
POST | /v1/, /resume, /run | Pause it, resume it, or run it once now |
GET | /v1/ | The domains that serve it |
POST | /v1/ | Serve it at a domain (connecting the domain if needed): the domain |
DELETE | /v1/ | Stop serving it there; the domain is kept |
GET | /v1/ | What it printed while serving (since, level, deployment_id, limit) |
GET | /v1/ | What it spent this month, against its budget: requests, cpu_ms, usd, budget_usd, limited_by and by_type |
POST | /v1/ | Its code, settings, secret names, bindings and bound data: a project.export operation |
A deployment's credential (NOCLICK_DEPLOYMENT_TOKEN in its environment) is a bearer that acts as a key of the app's user, attributed to the app: GET /v1/projects/current reads the app, its bindings resolve, and every other route a key of that user reaches answers it, except: what the gate keeps for a person (403 permission_denied: keys, grants, sessions, caps and budgets, policy, approvals' answers, raw tokens, secrets' values, accepting terms), what files a person's details with a carrier (documents, a port, a texting registration), turning on a domain's auto-renew, and the areas beyond the app (quotes, humans, users, tool_calls, approvals, trash, policy). A purchase (POST /v1/phone_numbers, POST /v1/domains) charges what it costs now within what's left of the app's budget (402 spend_cap_exceeded with remaining_usd, else), is the app's, renews within its budget, and one in a person's name answers 402 approval_required until they approve it. Calls, texts, automations, hosted MCP servers, channels, machines and cards count toward the app's budget and wait on it; of workflows and workspaces it reaches only what its code made. It reads its own user's events, webhooks, usage and the attachments its code uploaded, and its own requests and operations, never changing them. It acts for the app's user alone: NoClick-User, and a user_id naming anyone else, answer 403. On a resource its own code didn't make it reads and uses (its data, a message, a run) but never changes or deletes it (403), and on one bound to the app it may also clear its data; another app, and what another app's code made (by id, or named in a body), answer 403 and 404; an operation it didn't start, 404. POST /v1/agents/{id}/messages (and /ask, /a2a) starts turns in the app's own threads, a conversation it sends being kept as app:<app id>:<key> (from a preview, app-preview:<app id>:<key>, its turn answered by a simulated world), and is refused (403) for an agent holding the noclick tool; POST /v1/agents and PATCH refuse giving an agent the noclick or agents tools, a person other than the owner, or a platform trigger. Once the app's budget or its user's cap is used up, a paid call answers 402 spend_cap_exceeded (scope project or user), and once your balance is, 402 payment_required, each naming the app in project and project_id. GET /v1/usage?group_by=project splits spend by app, and GET /v1/usage/limits adds projects.
App services
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | Turn on transactional email, from your domain or the default sender |
GET | /v1/ | The sender and the DNS records it waits on |
POST | /v1/ | Send one email |
POST | /v1/ | Send a one-time sign-in code, on a lane of its own |
POST | /v1/ | Broadcast to up to 50,000 opted-in addresses: a project.email.broadcast operation |
GET | /v1/ | Emails sent, with delivery, opens and bounces |
POST | /v1/ | Lift an address's broadcast unsubscribe after a confirmed opt-in |
POST | /v1/ | Turn on payments in a country, in test mode, with a link to claim the account |
GET | /v1/ | Test, claimed (and its step), live or disconnected |
POST | /v1/ | Commit sign-in code into the app's repo (providers): a project.sign_in.enable operation |
POST | /v1/ | Move the sign-in code to the version NoClick ships now: a project.sign_in.upgrade operation |
GET | /v1/ | The providers on, its tables, library version and what it still needs |
Payments aren't switched on yet: until they are, POST /v1/projects/{id}/payments answers 409 not_available.
Domains
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | Names for sale, with prices for the TLD's shortest term, its terms and any trademark claims_notice (q, tlds) |
GET | /v1/ | The domain registration agreement every registrant accepts: its url and version |
GET | /v1/ | List your domains |
POST | /v1/ | Buy one, charged to your account (name, max_price_usd, years (the TLD's minimum when omitted), registrant with its agreement_accepted_at, accept_claims; with NoClick-User, registrant is required): a domain.buy operation |
POST | /v1/ | Connect a domain you own elsewhere; it waits on the DNS records it returns |
GET | /v1/ | Whether a domain registered elsewhere can move to NoClick now, and its price (name) |
POST | /v1/ | Move one here from its registrar, charged to your account like a purchase (name, auth_code, max_price_usd, registrant): a domain.transfer_in operation |
GET | /v1/ | Read one: status, expiry, records, what it serves |
PATCH | /v1/ | Turn auto-renew on or off (auto_renew), or update the registrant |
DELETE | /v1/ | Disconnect a connected domain (a bought one lapses or transfers out) |
POST | /v1/ | Check its DNS again |
GET | /v1/ | A bought domain's DNS records |
POST | /v1/ | Add a record (type, name, value) |
DELETE | /v1/ | Remove a record |
POST | /v1/ | Renew it (years, the TLD's minimum when omitted; max_price_usd): a domain.renew operation |
POST | /v1/ | Move a transferred domain's DNS to NoClick, answering the records it copied |
POST | /v1/ | Unlock it and get its transfer code |
Browsers
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | List profiles |
POST | /v1/ | Create one: a person's saved logins (name, region) |
GET | /v1/ | Read one, with the sites it's signed in to |
DELETE | /v1/ | Delete it and its logins |
POST | /v1/ | Send the person a live browser to sign in to a site once: a browser.login operation |
DELETE | /v1/ | Forget one site's login (a short browser clears it; returns when done) |
GET | /v1/ | List browsers |
POST | /v1/ | Start a browser (profile_id, timeout_s, region, solve_captchas, human) |
GET | /v1/ | Read one: its URL, status and live view |
DELETE | /v1/ | Close it |
POST | /v1/ | Open a URL |
POST | /v1/ | Do a task in plain words (max_steps, files): a browser.act operation, 202 |
POST | /v1/ | Read structured data off the page (schema) |
GET | /v1/ | A PNG of the page |
GET | /v1/ | The page as Markdown, text or HTML |
Cards
These take the cards scope. See Cards.
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | Where your company's card issuing stands: status, requirements_due, onboarding_url |
GET | /v1/ | Every merchant category name with the codes it covers |
GET | /v1/ | List cards, newest first (status, agent_id, user_id) |
POST | /v1/ | Issue a virtual card with a limit, merchants and an agent |
GET | /v1/ | Read one: limit, spent, status |
PATCH | /v1/ | Change its limit, merchants, approval threshold, agent or name |
POST | /v1/ | Approve before paying: a card.purchase operation whose result is a number for exactly that purchase |
GET | /v1/ | Its charges, declines and refunds, newest first |
GET | /v1/ | Read one |
POST | /v1/ | Dispute a settled charge |
POST | /v1/ | A page that shows its number and CVC, briefly, to whoever opens it |
POST | /v1/, /unfreeze, /close | Freeze it, unfreeze it, or close it for good |
GET | /v1/ | The balance cards spend from, and where to top it up |
POST /v1/agents and PATCH /v1/agents/{id} take card_id, the card the agent holds and pays with (null takes it back; giving one takes the cards scope), and an agent carries it.
Cards are in invite-only beta. Without access, GET /v1/cards/program, GET /v1/cards/funding, POST /v1/cards and an agent's card_id answer 409 not_available, with no program_status; the other routes answer as for an account with no cards, and merchant_categories lists the categories. With access, issuing a card or reading funding before your company's program is active answers not_available with program_status and requirements_due.
Invite-only beta routes
They follow the same conventions.
Phone numbers
Porting a number in, beside the phone number routes that work today. A ported number's source is ported.
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | Port one in from its carrier (number, account_holder, service_address, account_number, pin, bill, requested_at, max_monthly_usd, name): a phone.port_in operation, awaiting_human until the account holder signs |
POST | /v1/ | Send a rejected port again with corrected details: a new phone.port_in operation |
Machines
| Method | Path | What it does |
|---|---|---|
GET | /v1/ | The sizes: name, cpu (vCPUs), memory_gb, disk_gb, monthly_usd, working_conversations |
GET | /v1/ | List machines (name, user_id) |
POST | /v1/ | Start one: name, size, region, volumes, network, max_monthly_usd |
GET | /v1/ | Read one: status (and status_reason), size, price, network, ports and how many agents run on it |
PATCH | /v1/ | name, size, volumes, network or max_monthly_usd; a larger size or new volumes restart it once running turns end, a smaller size moves it to a new host |
DELETE | /v1/ | Delete it with its disk (409 while agents run on it) |
POST | /v1/ | Run a command on the machine itself (background for a machine.exec operation) |
PUT / GET | /v1/ | Write or read a file on its disk |
POST | /v1/ | Serve a port at an HTTPS URL (port, public); a private one asks for a key with the machines scope |
DELETE | /v1/ | Stop serving it |
GET | /v1/ | The agents that run on it |
POST | /v1/ | Restart it: a machine.restart operation |
POST | /v1/, /start | Stop it (its agents take no turns) or start it again |
Every create of a resource that holds data or hosts code (databases, apps, machines; buckets, dicts and queues already do) takes region, fixed for its life. An agent takes machine_id to run on a machine.
Agents
Beside the agent routes that work today: a connection from another scope, another user's or the account's, is connection:<id> (or connection:<id>.<operation>), accepted only on a key acting for the whole account; POST /v1/toolkits/call then takes the toolkit's tools too, so the tool's name resolves to that connection.
Export
| Method | Path | What it does |
|---|---|---|
POST | /v1/ | Everything a user has, in portable formats: a user.export operation; {delete_after: true} deletes the user once it's stored |
POST | /v1/, /v1/, /v1/, /v1/ | One resource in its own format: a bucket.export, volume.export, dict.export or queue.export operation |
An export is an operation: read it (and its bundle, once it succeeded) with GET /v1/operations/{id} or /wait. Its result is an export: url (the zip, valid until expires_at), size_bytes, contents (each {kind, name, detail}) and remaining_dependencies. A second export of the same user or resource while one runs answers 409 with its operation_id.