Skip to main content
The CLI stores credentials as named profiles — each holds either an OAuth session or an API key, so you can switch between teams or workspaces without re-entering credentials. This page is the reference for managing those profiles and the teams they act on. For first-time sign-in, see Sign in.

Profiles

Use a different profile for a single command

--profile <name> works on every command and doesn’t change your default.

Sign out

duvo logout revokes the OAuth tokens server-side and removes the profile locally.

Environment variables

For non-interactive use (CI, scripts), the CLI also reads:

Teams

A profile’s credentials are scoped to a team. Use these commands to confirm which team you’re acting on and inspect its members before running commands that change data.
If --team is omitted, member listing uses the team scoped to your API key or active profile. Add --json to any command for machine-readable output. The org-* commands are the exception to the team scoping above. They read across every team in the organization, so a key pinned to a single team is rejected — use one created with access set to all teams you can access, and an organization Admin, Executive, or Owner role. org-runs carries one time window per lifecycle timestamp, and they are not interchangeable. Reach for --created-after / --created-before to ask “everything in this period”: every run has a creation time, so that window drops nothing. --started-after / --started-before excludes runs that never started — pair both ends with --status running to bound a stuck-run sweep, since a lower bound is what stops it also counting runs left running months ago. --completed-after / --completed-before filter on completion, so they never match a run that is still going — they are also accepted as --since / --until, the names duvo runs list ships, but the column-named pair is the one to reach for here, where all three windows exist side by side. Every bound is half-open ([after, before)), so consecutive windows tile without counting a run twice, and the three windows combine. With no time bound at all, org-runs returns the last 7 days of runs and says so (default window: last 7 days in the summary line). An organization’s history is unbounded and a monitor wants what is recent, so the default keeps an unfiltered poll cheap however long the organization has been running. Pass any bound — on creation, start or completion — to replace it. org-runs does not count matches unless asked: without --include-total the response carries no total, so a full page is reported as More may be available alongside the --offset that reaches the next one. Pass --include-total for Showing X of Y — it walks every match, which is the expensive half of an org-wide query. org-runs pages up to 500 runs at a time (--limit 500), five times the team runs list cap, because pulling an organization’s history is rate-bound rather than query-bound: the API allows 100 requests a minute per key, so page size decides how fast a month of runs comes back. To read more than one page, follow the cursor: every page that has a successor prints Next page: --cursor <value>, and passing that value (with the same filters) returns exactly the runs after the one you last saw. There is no --offset here, unlike runs list and teams org-agents: runs are created constantly, so an offset sweep can repeat a run at a page boundary or, on a --status needs_attention poll where runs are also being resolved, skip one. A cursor does neither. Agents are a small, slow-changing directory, which is why that list keeps --offset.

Manage people already on the team

These change who is on the team and what they can do. duvo invite is for people who haven’t joined yet.
Pass --team <team-id> to act on a team other than the active one, and --json for machine-readable output. Get member ids from duvo team members. Both remove-member and leave ask for confirmation before acting; pass --yes to skip the prompt in scripts. In a non-interactive shell they refuse rather than treat piped input as consent.

Invite people

duvo invite covers both team and organization invitations. Team commands honour --team; invite org-member takes the organization id positionally and needs an org Admin, Executive, or Owner role.
duvo teams invite-org-member was removed in 1.46.0 with no alias. Use duvo invite org-member <org-id> instead.
Creating an invitation does not email anyone. duvo invite create and duvo invite org-member only send mail when you pass --send-email. Without it the invitation exists but nobody is notified; run duvo invite resend <invite-id> to deliver it. duvo invite bulk always emails.
When you ask for an email and it does not go out, the command still prints the invitation ID and then exits non-zero, so a script can tell that the delivery failed. If the failure was ambiguous — a timeout or a gateway error, where the server may already have sent the mail — the message says so rather than claiming nobody was notified; check with the recipient before resending, or you may deliver a second invitation. duvo invite bulk exits non-zero when any invitation was rolled back.
No command takes a frontend URL. The accept link is built server-side from the environment’s configured app origin, so an invitation email always points at the real Duvo app.

Confirm your team before a destructive command

Running this first is a cheap way to make sure a bulk delete or delegate lands on the team you intend.

Duvo CLI

installation and first sign-in

Scripting and CI/CD Patterns

authenticating with API keys in pipelines