About duvo-cli
duvo-cli is the command-line interface for Duvo's public API, aimed at automating and scripting Duvo workflows. It combines resource-specific commands with a low-level API command and JSON output for scripts.
Key features
- 01Manage agents, runs, cases, and automations
- 02Use JSON output for scriptable responses
- 03Call endpoints through the low-level API command
- 04Manage authentication profiles and teams
- 05Work with queues, files, integrations, and skills
Use cases
- 01Automate Duvo tasks in CI or scheduled scripts
- 02Create agents and start or manage runs
- 03Query API endpoints without a dedicated command
- 04Process cases and queues from the command line
MCP support
The CLI documentation mentions authorizing a custom MCP server as part of an end-to-end workflow. It does not describe the CLI itself as an MCP client or server.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ duvoai/ skills --skill duvo-cliAfter installing, type/duvo-cliin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/api-escape-hatch.mdcommands.mdworkflows.mdDetails
From the project READMEDuvo CLI
duvo is the official command-line interface for the Duvo
public API. It is published to npm as @duvoai/cli:
a single binary with resource-grouped subcommands (duvo agents …,
duvo runs …, duvo cases …, …) and a low-level duvo api <method> <path>
escape hatch for endpoints that don't yet have a dedicated command.
Use the CLI when the user wants to automate or script Duvo. The
web UI at https://app.duvo.ai is the right answer when they want to
click around interactively; duvo is the right answer the moment they
ask for a shell snippet, a cron job, a CI step, or "how do I do X
without opening the browser?". Both surfaces talk to the same public
API — there is no functional gap to bridge by reaching for curl.
Managed sessions and manual setup
In a managed session, the host runtime instructions govern transport, setup, reference lookup, and scope. Follow them before this standalone guide. When the host says the CLI is installed and configured, do not probe installation, log in, inspect credentials, change profiles, or switch transport. Read the references it names before commands. The installation, authentication, and profile-management steps below are for manual standalone setup only.
Install (manual setup)
npm install -g @duvoai/cli
duvo --version
Requires Node.js ≥ 22.22.0. npx @duvoai/cli <cmd> also works without
a global install.
Authentication (manual setup)
duvo keeps credentials as named profiles on disk. One profile per
(team, environment). The first profile added becomes the default; from
then on, every command uses the default unless overridden.
duvo login # OAuth flow — opens your browser
duvo login --name acme # name the profile up front
duvo login --api-key <key> # skip OAuth and store an API key directly
duvo whoami # confirm who's signed in
duvo login defaults to browser-based OAuth against the Duvo
production tenant. Pass --api-key <key> to store a long-lived API key
instead (grab one from https://app.duvo.ai/settings/api-keys).
Switching between profiles:
duvo profiles list # show all profiles; `▶` marks the default
duvo profiles use # interactively pick the default profile
duvo profiles use acme # set a named profile as the default
duvo --profile acme whoami # one-off override for a single command
Resolution precedence (the first match wins):
- Profile:
--profile <name>flag →DUVO_PROFILEenv →defaultProfilein config. - Credential:
DUVO_API_KEYenv (bypasses the profile's stored credential lookup) → the profile's stored credential. - Base URL:
DUVO_API_BASE_URLenv → the profile'sapiBaseUrl→https://api.duvo.ai. - Team:
--team <id>flag →DUVO_TEAM_IDenv → the profile'sdefaultTeamId→ the team derived from the active credential. Team-scoped API keys reject a different--team; user-scoped keys can select any team the user can access.
Reading the help
Read references/commands.md for command syntax. Every command also supports
--help; in managed sessions, use it only when the host runtime permits it.
For standalone use, these help commands describe the installed binary:
duvo --help # global help, lists every top-level group
duvo agents --help # subcommand group
duvo agents create --help # exact flags for one command
If the binary rejects a documented flag, its help describes the installed version. Do not guess flags or treat standalone help examples as instructions to probe a managed session.
Output modes
Each command picks a default output shape based on what it returns:
- Single resource (
duvo agents get <id>,duvo runs get <id>) → human-readable key/value text. - Collection (
duvo agents list,duvo runs list) → table with a fixed column set per resource. --jsonon either of the above → raw JSON to stdout. This is the stable contract for scripts — table and text formats can change between versions, JSON is safe to pipe intojqoryq.
duvo agents list --team <target-team-id> --json | jq '.agents[].id'
For scripting, pass --json wherever the command reference lists it and parse the response. Don't
parse the table output — column widths and truncation thresholds are
not part of the public contract. Envelope keys differ across
endpoints — agents list returns .agents[], queues list returns
.queues[], runs list returns .data[], agents create returns
.agent.id. Inspect the shape with a one-off --json call before
hard-coding a jq path.
The duvo api escape hatch
Any endpoint in the public API is reachable via duvo api <METHOD> <path>, modelled directly on gh api. Reach for it when:
- A new public endpoint shipped but doesn't have a dedicated
duvocommand yet. - You need a very narrow query-string or body shape that the high-level command doesn't expose.
- You're debugging —
duvo apishows exactly what the API returned, with no formatting in the way.
duvo api passes the path through verbatim — it does not resolve
--team/DUVO_TEAM_ID/the profile default the way the high-level commands
do, so spell out whatever the endpoint's own path is. Route shapes vary
per resource: some collections are team-scoped
(/v2/teams/<team_id>/agents), others are flat (/v2/artifacts), and
some by-ID routes stay team-scoped (/v2/teams/<team_id>/invites/<id>).
Take the exact path from GET /v2/documentation/json (the raw OpenAPI
document; /v2/documentation is the Swagger UI HTML), don't infer it.
duvo api GET "/v2/teams/$TEAM_ID/agents" -F limit=20 -F offset=0
duvo api POST "/v2/teams/$TEAM_ID/agents" -f name="Ops bot" -F [email protected]
duvo api POST "/v2/teams/$TEAM_ID/agents" --input body.json
cat body.json | duvo api POST "/v2/teams/$TEAM_ID/agents" --input -
-f sends the value as a string; -F parses it as a typed value
(boolean, number, JSON null, or @file to load JSON from disk). See
references/api-escape-hatch.md for the full flag table.
High-level commands like duvo agents create are sugar over duvo api — they share the same HTTP client, output pipeline, and exit
codes, so a script can mix the two freely.
Resource groups
See references/commands.md for the full command tree with flags.
The top-level groups are:
- Auth & profiles —
login,logout,whoami,profiles … - Automations —
automations …(list, create, get, current, flow, rename, duplicate, delete, add/remove agents and queues, tabs) and their Policy:automations config-fields,add-config-field,edit-config-field,set-config-field,hide-config-field,restore-config-field,config-sections … - Agents —
agents …,agents delete,agents move,agents duplicate/clone,agents models,agents set-model,agents case-triggers …,agents schedules …,agents triggers …,agents slack-triggers …,agents memory …,agents eval-scores,agents eval-rubrics … - Suggestions —
suggestions …(Connection suggestions: list, consume, reject) - Agent folders —
agent-folders …(organize agents in a tree) - Revisions —
revisions …,revision-integrations …(versioned configs) - Runs —
runs …(start, get, message, stop, respond to HITL, evaluation) - Queues & cases —
queues …,queues eval-rubrics …,queues aggregations …(Cube-style aggregations over typed Case data),queue-labels …,cases …,cases search - Notifications —
notifications …(list, get, feed, get-batch, counts, unread-count, mark-read, mark-batch-read, mark-all-read, dismiss, delete-read, delete-all) - Files & sandboxes —
files …,sandboxes … - Connections & integrations —
integrations …,connections …,oauth … - Secrets & credentials —
secrets …(env-var secrets),credentials …(browser logins),revision-secrets …,revision-logins … - Clarity —
clarity …(process search, versions, captures, gaps, evidence, facets, export, generation, promotion, artifact imports, invite links, doctor, process landscape, process links, process tags, process summaries) - Pulse —
pulse …(create, get, list, send message, attach files, refresh, stop, rename, share, duplicate, move to another team, pdf/snapshot export, version history, restore, messages, answer HITL, connections, comment threads, delete Pulse dashboards,pulse data …to connect/disconnect queue data sources) - Skills & plugins —
skills …,plugins … - Team —
team current,team get,team members,team set-role,team remove-member,team leave,team use,teams list,teams org,teams orgs,teams create-org-team,teams create-workspace,teams org-agents,teams org-insights,teams org-metrics,teams org-runs,teams org-usage - Invitations —
invite list,invite create,invite bulk,invite update,invite resend,invite delete,invite org-member,invite link get|create|delete - Bundled guides —
guide …(version-matched CLI guides for AI agents) - Self-update —
update(update the installed CLI to the latest version) - Low-level —
api <method> <path>
For end-to-end recipes (create an agent → start a run → respond to
HITL; manage a queue and its cases; upload files for a sandboxed run;
authorize a custom MCP server) see references/workflows.md.
Automations
On a team that has moved to Automations, every agent and queue belongs to an automation: the container the web app shows for one process.
duvo automations create --name <name>creates the automation and its first agent. The--jsonresponse carriesautomation.idandfirst_agent_id. Configure that first agent instead of creating another.duvo queues createrequires--automation-id <id>. Without it the API returns 409: "This team has moved to Automations, so a new agent or queue must be created inside one."duvo agents createaccepts the same--automation-id. Without it the backend creates a new automation named after the agent, so pass the flag to keep every agent of one process in one automation.duvo automations listgives the IDs;duvo automations current <id>shows the agents, queues, triggers and schedules the automation runs now.
On a team that hasn't moved, automations commands return 403 ("This team
hasn't moved to Automations, so it has no automations to work with.") and
agents and queues are created without the flag.
Recommended build order
When building a process from the CLI, work in this order. Each step depends on the one before it.
- Connections. For every service the agents use, check
duvo connections list --team <target-team-id> --type <slug>. Connect what is missing first (see "Native OAuth provider keys" below). A connection that doesn't exist can't be pinned later. - Automation.
duvo automations create --name <name> --json. Keepautomation.idandfirst_agent_id. - Queues, schema, labels.
duvo queues create --name <name> --automation-id <id>, thenduvo queues schema create <queue-id> --file schema.json --name <name>for typed cases, thenduvo queue-labels create. Schema rules and an example are in workflow 4 ofreferences/workflows.md. - Agents. Create the remaining agents with
--automation-id, attach integrations, pin a connection to every OAuth or user-provided slot, and wire queues withduvo queues link-agent(--enable-triggerfor a consumer, or its case trigger starts off). Answer every Policy question the agents read (see "Policy (config fields)" below). Run the setup checklist in workflow 4 ofreferences/workflows.mdbefore going on. - Case triggers on.
duvo agents case-triggers list <consumer-agent-id> --jsonmust showenabled: true. If it showsfalsewithpending_changeset, the team saves automation changes to a draft: the trigger switches on only when someone clicks Make active in the automation's header in the Duvo app, and the CLI has no command for it. Ask the user to do that; until then a test case stays Pending. - Test run.
duvo runs start --agent <id>, or add one test case to the queue, and read the outcome withduvo runs get <run-id>. - Schedule, created paused. Pass the user's IANA timezone explicitly:
Whoever creates the schedule receives that agent's approval requests (see "Approvals go to the Run's author" below).duvo agents schedules create <agent-id> --frequency workday --time 07:30 \ --timezone Europe/Prague --disabled duvo agents schedules update <agent-id> <schedule-id> --enable # after the test run is right
Policy (config fields)
An automation's Policy holds typed answers such as thresholds,
mailboxes and sender lists. Put values a person will tune there, not in AOP
text. An agent reads a field when its AOP contains
<DUVO:CONFIG key="<key>" name="<label>">, and each run is handed the
current answers, so changing an answer needs no new revision. A run is
refused while a field it reads has no answer.
Agents can't write Policy. Values change on the Policy screen, with
duvo automations set-config-field, or through Ask Duvo on a user's
instruction. Rules an agent learns while running belong in a file or in
agent memory. Commands, answer types, value shapes, --fill-mode and
--binding are in references/commands.md under "Policy (config fields)".
Invite people to a Clarity interview
Prefer duvo clarity interview-invites send <process-id> with repeated --invitee
flags for multiple interview recipients: at most 25 unique recipients per batch.
invite bulk is team-only; use invite create for --no-interview or --process-role.
Read the interview invitation workflow
for recipient selection and delivery handling; avoid one-by-one loops.
Conventions you can rely on across commands
These hold for every command group, so call them out when explaining the CLI rather than restating per-command:
--profile <name>is a global flag that overrides the default profile for that single invocation.--team <id>is a global flag that overrides the resolved team for commands that use request-team scope. In managed sessions, always pass the target team explicitly for those commands, even when it matches the configured default. Follow the host instructions to choose the target. Resource-ID commands may ignore this flag; raw API paths are not rewritten. Command-local ownership flags (including landscapepropose-process --team) assign an owner and must not receive a default team mechanically. Organization commands use--orgor a positional organization ID as documented. OAuth and user-scoped API-key profiles can select an accessible team; team-scoped API-key profiles reject a different team.--jsonis available on nearly every command that hits the API and is the shape to use in scripts. Use it only when the command's help or command reference lists it; never invent an unsupported flag or parse table output.- Destructive operations (
duvo logout,duvo profiles remove,duvo runs stop,duvo agents delete,duvo agents move,duvo agent-folders delete,duvo cases delete,duvo cases clear,duvo cases bulk-delete,duvo cases bulk-reprocess,duvo cases bulk-update-status,duvo cases bulk-update-priority,duvo skills delete,duvo skills revisions delete,duvo queues delete,duvo secrets delete,duvo automations delete,duvo automations remove-agent,duvo automations remove-queue,duvo credentials delete,duvo revision-secrets detach,duvo revision-logins detach,duvo revision-integrations remove,duvo revision-integrations connections unpin,duvo agents schedules delete,duvo agents case-triggers delete,duvo agents triggers delete,duvo agents slack-triggers delete,duvo agents eval-rubrics remove,duvo agents eval-rubrics replace,duvo queues eval-rubrics remove,duvo queues eval-rubrics replace,duvo connections delete,duvo queue-labels delete,duvo queues aggregations delete,duvo integrations custom delete,duvo clarity process-labels delete,duvo suggestions reject,duvo files delete,duvo pulse delete,duvo pulse move,duvo notifications delete-read,duvo notifications delete-all,duvo team remove-member,duvo team leave, …) prompt for confirmation in a TTY and refuse on a non-TTY stdin. Pass-y/--yesto skip the prompt — never pipeyesinto the CLI to bypass the prompt; it explicitly refuses inferred consent from piped input. - Exit codes:
0success,1generic error,2auth error (missing or invalid key, unknown profile),3not found. Useful for CI scripts that want to branch on the failure mode. - IDs are printed bare (no quotes) so they copy cleanly into shell
pipelines and
$()substitutions:agent=$(duvo agents create --team <target-team-id> --name "Ops bot" --input "What to do" --json | jq -r .agent.id) duvo runs start --agent "$agent" - Deprecation warnings print to stderr (so they don't corrupt
--jsonstdout) and can't be silenced. If a user reports an unexpected warning, check whether their@duvoai/cliversion is behind.
Pitfalls and gotchas
- Profile vs credential.
--profile foochanges which profile is read;DUVO_API_KEY=…overrides the credential but still uses the selected profile'sapiBaseUrl. To target a totally different environment, set bothDUVO_API_KEYandDUVO_API_BASE_URL. profiles usewith no argument opens an interactive picker that only works in a TTY. In CI, always pass a name explicitly.agents createis interactive by default. When--nameand--inputare omitted and stdin is a TTY, the CLI prompts. In CI, pass both flags (and--no-buildif you don't want an initial build) so it never blocks waiting for input.cases create --from-file -reads JSON from stdin. The file (or stdin) is a single case object or an array of up to 100 cases. Each case is{ "title": "…", "data": "…", "labels": [{ "key": "…", "value": "…" }] }. Labels with nokeyare tag-only labels.- Sandbox files vs team files.
duvo files …manages persistent team files (visible in the Files surface in the UI);duvo sandboxes …stages files for a single run (duvo runs start --sandbox-id <id>). Don't mix them. - Connections vs integrations.
duvo integrations listshows the team's catalog of integration types;duvo connections listshows the user's actual connected accounts (one per OAuth/credential flow). OAuth-based connections (Gmail, Slack, …) are started byduvo oauth native start <provider>, not byduvo connections create— the latter is only for user-provided MCP servers. - An empty
connections listis per user and per team. It lists the calling user's connections in the selected team, plus connections shared with that team.integrations listrows always showhas_oauth_tokens: false: they are catalog slots, not accounts. So integrations listed with 0 connections means this user has authorized nothing in this team yet. Checkduvo whoamiand the--teamyou passed before concluding the account is missing. - Native OAuth provider keys are not integration types.
oauth native starttakes the provider key, whichintegrations list --jsonreports asoauth_provider_key(Gmail isgoogle_gmail, Google Calendar isgoogle_calendar, Outlook ismicrosoft_outlook). Passing the type (gmail) returns "Unknown OAuth provider". Anullkey means the integration isn't a native OAuth one. HubSpot, Asana, Attio, Amplitude, Supabase, Notion, Granola and Apify connect through MCP OAuth instead; the server URL and--integration-typefor each are inreferences/commands.mdunder "OAuth flows":duvo oauth mcp authorize --url https://mcp.hubspot.com --name HubSpot --integration-type hubspot. Connect anything else with anullkey in the web app. - Attached ≠ connected.
revision-integrations attachonly creates the integration slot on the revision. For OAuth and user-provided integrations (HubSpot, Gong, Slack, custom MCP, …) the run can only use the slot once one of the user's connections is pinned to it — an attached slot with no pinned connection fails at runtime with "not connected", and nothing warns you at attach time. Find the connection withduvo connections list --team <target-team-id> --type <slug>, pin it withduvo revision-integrations connections pin, and verify withduvo revision-integrations connections list(expect ≥ 1 entry per slot). Default integrations (browser, Exa, human-in-the-loop,case-queue-producer/case-queue-consumer, …) need no pin — but case-queue slots need a queue mapped instead, verified withduvo revision-integrations case-queue-setup. See workflow 7 and the setup checklist in workflow 4 ofreferences/workflows.md. - A queue with no consumer keeps its cases in Pending. Nothing claims
them, so they stay there until a person closes them by hand
(
duvo cases bulk-update-status). Don't create an "audit" or "log" queue that only a producer writes to. For a record of what happened, have the agent write a file (duvo files …) or add one digest case per run to a queue that does have a consumer. - Approvals go to the Run's author. A human-in-the-loop request lands with whoever the run belongs to: a scheduled run belongs to the person who created the schedule, and a case-triggered run belongs to the owner of the previous run on that case, falling back to whoever created the case. A schedule created with an API key belongs to the key's user. The request lands in that person's Requests inbox, so create schedules as the person who should answer the approvals.
- Secrets vs connections vs credentials. Three separate stores:
duvo secretsholds env-var key/value pairs injected into runs at runtime;duvo credentialsholds browser logins (domain + password- optional TOTP) used by the browsing agent;
duvo connectionsholds OAuth/API-key connections to external services (Gmail, Slack, custom MCP, …). They don’t overlap — attach secrets withrevision-secrets, logins withrevision-logins, and connection slots withrevision-integrations connections pin.
- optional TOTP) used by the browsing agent;
- Multi-team OAuth and user-scoped API-key profiles. An OAuth login,
or an API key not pinned to a single team by the server (a user-scoped
key), can act on several teams. Use
duvo teams listto see all teams, then pass--team <id>on request-scoped commands. In manual setup,duvo team use <id>can set the profile default — both work for these profiles. A team-scoped API key is the one exception: the server already pins it to one team, so it cannot target a different team via--team. - Reconnecting an expired OAuth connection. Don't create a new
connection for a stale one — pass
--reconnect-instance-id <id>toduvo oauth native startorduvo oauth mcp authorizewith the existing connection's ID (fromduvo connections list) to re-authorize it in place instead of creating a duplicate. - Clarity has both read and explicit write commands. Start with
duvo clarity overview <process-id>, then useversions,current,proposal,compare,gaps,evidence,readiness, orfacetsto understand the process. Use the write commands only when the user actually wants to mutate Clarity state: generation, promotion, revert, postprocessing, automation build, extra-capture agent, invite links, or Miro artifact imports. Those v2-only commands require a Clarity v2 process; legacy v1 processes support onlyoverview,status,captures,capture, andexport. Default output is compact; transcripts and media URLs are included only when a JSON command explicitly passes--include-transcripts. - Notifications: batches vs individuals.
duvo notifications feedreturns a mix of individual notifications and notification batches grouped by Agent or Queue;get-batchandmark-batch-readonly accept a batch ID (fromfeedor a notification'sbatchId), not an individual notification ID. Onnotifications list,--sort importancerequires--batch— the CLI rejects the combination locally if--batchis omitted. - Clarity artifact imports use a two-phase API under the hood. Use
duvo clarity import-artifact <process-id> <file>for local Miro SVG, XML, PNG, or JPEG exports. It creates the signed URL, uploads bytes, and completes the import. For custom upload clients or artifact-chat workflows, useduvo apiagainst the public route directly. - Slack channel triggers need a connected workspace first.
agents slack-triggers createfails unless the Slack workspace is already bound to the agent — check withagents slack-triggers workspaces <agent-id>before creating, and pass--workspacewhen the team has more than one installed workspace. - Team member management has per-command role floors.
team set-roleneeds Manager and above (only an Owner can grant or remove the Owner role);team remove-memberneeds Superadmin and above;team leaveworks for any member. All three refuse to act on the last remaining Owner. - Two ways to attach a file to Pulse.
duvo pulse send-message <id> --message <text> --attach-file <path>uploads and references a file in one step — use it when you already know the instruction.duvo pulse attach <id> <file>only stages the file and prints its attachment ID, for when the instruction is composed or sent separately (e.g. from a script building the message body). Both share the same 5-file, 25MB-each limit.
When the CLI is the wrong tool
Steer users to a different surface in these cases:
- They want to read a stream of run events as they happen. The
CLI's
runs get/runs messagesare point-in-time reads. Use--webhook-urlonruns startto receive events asynchronously, or open the run in the web UI. - They want to edit an AOP or agent configuration interactively.
revisions createandrevisions updateaccept a config file, but composing the config by hand is painful. To change only the AOP, pass a markdown file with--aop-fileinstead. Direct them to the Agent editor in the web UI for anything beyond a small targeted patch. - They want to run an agent in their own process rather than on Duvo. That's not what the public API exposes; the CLI is a client to the hosted Duvo platform.
In-skill references
references/commands.md— full command tree with flags, grouped by resource. Use this when you need the exact flag a command expects.references/workflows.md— end-to-end recipes (create an agent and start a run; decisions through HITL; queue + cases lifecycle, case schemas and dedupe; sandboxed runs with uploaded files; authorize a custom MCP server).references/api-escape-hatch.md—duvo apiflag reference, with worked examples of-fvs-F,@filetyped fields, and--inputfor raw JSON bodies.
Read the relevant command reference before acting; managed sessions follow the host runtime instructions for any help fallback.
See also
aop-writer— author or rewrite the AOP that ships in a Build (duvo revisions create/duvo revisions update).run-debugger— diagnose a failed Run; pairs withduvo runs get,duvo runs messages, andduvo revisions getfor API-mode reads.workflow-debugger— audit an Agent or workflow across many Runs; pairs withduvo runs list,duvo queues agents, andduvo revisions get.
Resources
- Duvo — product website
- Duvo documentation — concepts, building Agents, Connections
- Duvo API reference — every endpoint the CLI wraps
@duvoai/clion npm — versions, install instructions, changelog- Web app — interactive surface for everything the CLI scripts
- API keys — issue and revoke API keys for
duvo login --api-key - Public skill repository — the MIT-licensed community release of this skill, packaged for installation in third-party Claude Code setups