← Back to skills
extension
Category: Development & EngineeringAPI key requirement unconfirmed

devic-cli

@devicai/cli reference — the Devic AI Platform CLI. Use when executing Devic API operations from the command line, scripting automations, or building agent workflows that interact with assistants, agents, tool servers, and feedback.

personAuthor: jakexiaohubgithub

@devicai/cli

CLI for the Devic AI Platform API. Agent-first — JSON output by default when piped, human-readable in terminal. Single runtime dependency (commander).

Installation

npm install -g @devicai/cli
# or
npx @devicai/cli <command>

Authentication

# Login with API key (validates with a test request, stores in ~/.config/devic/config.json)
devic auth login --api-key devic-xxx

# Check auth status
devic auth status

# Logout (removes stored credentials)
devic auth logout

Environment variables override stored config:

| Variable | Description | |----------|-------------| | DEVIC_API_KEY | API key | | DEVIC_BASE_URL | API base URL (default: https://api.devic.ai) |

Global Options

| Option | Description | |--------|-------------| | -o, --output <format> | Output format: json or human. Auto-detected: JSON when piped, human in TTY | | --base-url <url> | API base URL. Overrides config and env var. Priority: --base-url flag > DEVIC_BASE_URL env > config file > https://api.devic.ai | | -V, --version | Show version | | -h, --help | Show help |

Exit Codes

| Code | Meaning | |------|---------| | 0 | Success | | 1 | Error | | 2 | Authentication required | | 3 | Poll timeout |

Errors are written to stderr. In JSON mode: {"error":"...","code":"..."}. In human mode: markdown-formatted error with bold label and inline code.

Human Output Format

When using -o human (or in a TTY), all output is markdown-formatted:

  • Lists: Rendered as markdown tables with | Header | ... | format
  • Details: Properties with **Bold Label:** value, IDs in `inline code`
  • Conversations: Messages labeled **USER:** / **ASSISTANT:** / **TOOL:**
  • Status indicators: [OK] success, [..] in-progress, [!!] needs attention, [XX] failed
  • Actions: Success messages prefixed with [OK]
  • Errors: Written to stderr with **Error:** message format
  • Pagination: Footer with **Total:** N | **Offset:** N | ...
  • Nested objects: Rendered as fenced JSON code blocks
  • Thread tasks: Displayed as [x]/[ ] checklists

This format is designed for both human reading and LLM/agent consumption.

JSON Input

All create/update commands support --from-json <file> (or - for stdin) for complex payloads:

# From file
devic agents create --from-json agent-config.json

# From stdin
echo '{"name":"My Agent"}' | devic agents create --from-json -

Pagination

All list commands support:

| Option | Default | Description | |--------|---------|-------------| | --offset <n> | 0 | Items to skip | | --limit <n> | 10 | Max items (max 100) |


Commands Reference

devic assistants

Manage assistants and chat conversations.

devic assistants list

List all assistant specializations.

devic assistants list [--external] [--offset <n>] [--limit <n>]

| Option | Description | |--------|-------------| | --external | Only show externally accessible assistants |

devic assistants get

Get details of a specific assistant.

devic assistants get <identifier>

devic assistants create

Create a new assistant.

devic assistants create [--name <name>] [--description <desc>] [--from-json <file>]

| Option | Description | |--------|-------------| | --name <name> | Assistant name | | --description <desc> | Assistant description | | --from-json <file> | Read full assistant config from JSON file (- for stdin) |

The --from-json payload supports all assistant specialization fields: name, description, presets, model, provider, imgUrl, state, availableToolsGroupsUids, enabledTools, accessConfiguration, widgetConfiguration, memoryDocuments, structuredOutput, guardrailsConfiguration, codeSnippetIds, availableSkillIds, subagentsIds, maxChatMessages, maxToolResponseInputTokens, contextManagement ({ fullContextTurnDepth?: number, alwaysIncludeUserMessages?: boolean } — send only the most recent N turns in full and summarize older ones).

devic assistants update

Update an existing assistant (partial updates supported).

devic assistants update <identifier> [--name <name>] [--description <desc>] [--enabled-tools <a,b,c>] [--all-tools] [--from-json <file>]

Same options as create, plus the tool selection flags below. Only provided fields will be updated.

| Option | Description | |--------|-------------| | --enabled-tools <a,b,c> | Replace the enabled tool names. Use "" to enable none | | --all-tools | Enable every tool of the assigned tool groups |

--enabled-tools and --all-tools are mutually exclusive. Pass neither and the current tool selection is left untouched — read it with devic assistants get, which reports it as all, none, or the explicit list.

Requires CLI 0.14.0 or newer. Earlier versions could only change the selection through --from-json.

devic assistants delete

Delete an assistant.

devic assistants delete <identifier>

devic assistants chat

Send a message to an assistant. Uses async mode with polling by default.

devic assistants chat <identifier> -m "message" [options]

| Option | Description | |--------|-------------| | -m, --message <text> | Required. Message to send | | --chat-uid <uid> | Continue an existing conversation | | --provider <provider> | LLM provider override (openai, anthropic, azure, google) | | --model <model> | Model override | | --tags <tags> | Comma-separated tags | | --wait | Async mode + poll for result (default: true) | | --no-wait | Synchronous mode — blocks until response | | --from-json <file> | Read full ProcessMessageDto from file (- for stdin) |

When --wait is active, status updates are emitted during polling:

  • JSON mode (-o json): NDJSON lines on stdout:
    {"type":"chat_status","chatUid":"...","status":"processing","timestamp":1234567890}
    {"type":"chat_status","chatUid":"...","status":"completed","timestamp":1234567891}
    
  • Human mode (-o human): Readable status lines on stderr:
    [..] Chat `550e8400...` — **processing**
    [OK] Chat `550e8400...` — **completed**
    

Chat polling: 1s initial interval, 1.5x backoff, 10s max, 5min timeout.

Status values: processing, completed, error, waiting_for_tool_response, handed_off.

Status indicators: [OK] completed/active, [..] processing/queued, [!!] paused/waiting, [XX] failed/error.

devic assistants stop

Stop an in-progress async chat.

devic assistants stop <identifier> <chatUid>

devic assistants chats list

List chat histories for an assistant.

devic assistants chats list <identifier> [--omit-content] [--offset <n>] [--limit <n>]

devic assistants chats get

Get a specific chat history.

devic assistants chats get <identifier> <chatUid>

devic assistants chats search

Search chat histories across all assistants with filters.

devic assistants chats search [options]

| Option | Description | |--------|-------------| | --assistant <identifier> | Filter by assistant | | --tags <tags> | Comma-separated tags | | --start-date <date> | Start date (ISO string) | | --end-date <date> | End date (ISO string) | | --omit-content | Exclude chat content | | --from-json <file> | Read filters from file |


devic agents

Manage agents, execution threads, and costs.

devic agents list

devic agents list [--archived] [--offset <n>] [--limit <n>]

devic agents get

devic agents get <agentId>

devic agents create

devic agents create [--name <name>] [--description <desc>] [--from-json <file>]
                    [--cron <expr> | --schedule-at <HH:MM> | --every-days <n>]
                    [--days <list>] [--at <HH:MM>] [--start-date <YYYY-MM-DD>]
                    [--timezone <tz>] [--no-schedule]
                    [--environment <environment>]

The --from-json payload supports all agent fields: name, description, assistantSpecialization (with presets, availableToolsGroupsUids, enabledTools, codeSnippetIds, model, provider, subagentsIds, contextManagement), provider, llm, maxExecutionInputTokens, maxExecutionToolCalls, evaluationConfig, subAgentConfig, periodicExecution, sandboxPreprovision, environmentId.

devic agents update

devic agents update <agentId> [--name <name>] [--description <desc>] [--from-json <file>]
                              [scheduling flags] [--environment <environment>]

Scheduling: the agent runs itself

Devic has a scheduler. An agent can wake itself up; you do not need an external cron poking threads create every morning. Three ways to say when, mutually exclusive:

# A 5-field cron expression
devic agents create --name "Daily report" --cron "0 7 * * 1-5" --timezone Europe/Madrid

# A time of day, optionally narrowed to weekdays
devic agents update <agentId> --schedule-at 09:00 --days monday,friday --timezone Europe/Madrid

# Every N days from an anchor date. Cron cannot express this: its day-of-month
# restarts every month, so "every 14 days" is not a cron expression.
devic agents update <agentId> --every-days 14 --at 08:30 --start-date 2026-01-06

# Off
devic agents update <agentId> --no-schedule

--timezone takes an IANA name and defaults to UTC, which is rarely what a business report wants. Bad input is refused before the request: an invalid time of day, an unknown weekday, or two scheduling modes at once.

Environment: the machine, secrets and tools

devic agents create --name "Reporter" --environment "Reporting box"
devic agents update <agentId> --environment null   # disconnect

Accepts an environment _id or its name. See devic environments below.

devic agents delete

devic agents delete <agentId>

devic agents threads

Manage agent execution threads.

devic agents threads create

Create and optionally poll a new thread.

devic agents threads create <agentId> -m "task" [options]

| Option | Description | |--------|-------------| | -m, --message <text> | Required. Initial message/task | | --tags <tags> | Comma-separated tags | | --wait | Poll until terminal state | | --from-json <file> | Read thread config from file |

When --wait is active, status updates are emitted during polling:

  • JSON mode (-o json): NDJSON lines on stdout:
    {"type":"thread_status","threadId":"...","state":"processing","tasks":[...],"timestamp":1234567890}
    {"type":"thread_status","threadId":"...","state":"completed","tasks":[...],"timestamp":1234567891}
    
  • Human mode (-o human): Readable status lines on stderr:
    [..] Thread `thread-456` — **processing** (tasks: 1/3)
    [OK] Thread `thread-456` — **completed** (tasks: 3/3)
    

Thread polling: 2s initial interval, 1.5x backoff, 15s max, 10min timeout.

Terminal states: completed, failed, terminated.

Actionable states (returned to caller): paused_for_approval.

devic agents threads list

devic agents threads list <agentId> [options]

| Option | Description | |--------|-------------| | --state <state> | Filter by state | | --start-date <date> | Start date filter | | --end-date <date> | End date filter | | --date-order <order> | Sort: asc or desc | | --tags <tags> | Comma-separated tags | | --omit-content | Exclude thread content from response (returns metadata and state only). Significantly reduces payload size for large thread lists |

devic agents threads get

devic agents threads get <threadId> [--with-tasks] [--grep <pattern>]

| Option | Description | |--------|-------------| | --with-tasks | Include task details | | --grep <pattern> | Filter thread content to only show messages matching the pattern (case-insensitive). Useful for finding specific data within large threads without scanning the full content manually |

devic agents threads approve

devic agents threads approve <threadId> [-m "message"]

devic agents threads reject

devic agents threads reject <threadId> [-m "message"]

devic agents threads pause

devic agents threads pause <threadId>

devic agents threads resume

devic agents threads resume <threadId>

devic agents threads complete

Manually set a thread's final state.

devic agents threads complete <threadId> --state <COMPLETED|FAILED|CANCELLED|TERMINATED>

devic agents threads evaluate

Trigger AI evaluation of a completed thread.

devic agents threads evaluate <threadId>

devic agents costs

Track agent execution costs.

devic agents costs daily

devic agents costs daily <agentId> [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD]

devic agents costs monthly

devic agents costs monthly <agentId> [--start-month YYYY-MM] [--end-month YYYY-MM]

devic agents costs summary

Get today's and current month's cost summary.

devic agents costs summary <agentId>

devic tool-servers

Manage tool servers, their definitions, and individual tools.

Three kinds of tool server, told apart by the type column: http (tool definitions you wrote, called against a base URL), mcp (a remote MCP server), and integration (an app connected in Devic — Gmail, Drive, HubSpot). An integration has no URL and no stored definition; its tools live in the connected app, and the target column names that app, with (not connected) when no account is linked.

Credentials come back masked (••••••••). Sending a masked value back in an update keeps the stored secret, so get → edit → update is safe; send a real value to change it.

devic tool-servers list

devic tool-servers list [--offset <n>] [--limit <n>] [--project <project>]

--project accepts an _id, an identifier, or a name.

devic tool-servers get

devic tool-servers get <toolServerId>

devic tool-servers create

devic tool-servers create [--name <name>] [--url <url>] [--description <desc>] [--from-json <file>]

The --from-json payload supports: name, description, url, identifier, enabled, mcpType, toolDefinitions, authenticationConfig, imageUrl.

url, each tool's endpoint and the advanced body template accept {{...}} template references resolved at call time, so one tool server can target a different upstream per environment instead of being cloned:

  • {{metadata.<field>}} — thread or chat metadata (bare {{field}} is the same thing)
  • {{env.<VAR>}} — env var of the Environment connected to the agent or assistant
  • {{fields.<apiName>}} — published-MCP connection field, resolved by the wrapper only

Unknown references resolve to an empty string. Do not confuse these with the ${arg} / {arg} placeholders in endpoint, which are tool arguments the model supplies.

devic tool-servers create --name "Billing API" --url 'https://{{env.BILLING_HOST}}'

devic tool-servers update

devic tool-servers update <toolServerId> [--name <name>] [--url <url>] [--description <desc>] [--enabled <bool>] [--from-json <file>]

devic tool-servers delete

devic tool-servers delete <toolServerId>

devic tool-servers clone

devic tool-servers clone <toolServerId>

devic tool-servers definition

Get the full tool server definition.

devic tool-servers definition <toolServerId>

devic tool-servers update-definition

devic tool-servers update-definition <toolServerId> --from-json <file>

devic tool-servers tools

Manage individual tools within a tool server.

devic tool-servers tools list

devic tool-servers tools list <toolServerId> [--available] [--limit <n>] [--cursor <cursor>]

Lists the tools the server exposes. On an integration, --available browses everything the connected app offers instead, with an enabled column marking which of them this server uses. That catalogue is paged — follow the --cursor the output suggests rather than counting against the total, which includes tools the app has deprecated and are not listed.

devic tool-servers tools get

devic tool-servers tools get <toolServerId> <toolName>

devic tool-servers tools add

devic tool-servers tools add <toolServerId> --from-json <file>

JSON structure:

{
  "type": "function",
  "function": {
    "name": "tool_name",
    "description": "What it does",
    "parameters": {
      "type": "object",
      "properties": { "param": { "type": "string" } },
      "required": ["param"]
    }
  },
  "endpoint": "/api/path/${param}",
  "method": "GET",
  "pathParametersKeys": ["param"]
}

devic tool-servers tools update

devic tool-servers tools update <toolServerId> <toolName> --from-json <file>

devic tool-servers tools delete

devic tool-servers tools delete <toolServerId> <toolName>

devic tool-servers tools test

Test a tool call with parameters.

devic tool-servers tools test <toolServerId> <toolName> --from-json <file>

The JSON file should contain the parameters object: {"city": "London"}.


devic integrations / devic triggers

Browse the catalogue of connectable apps (Gmail, Drive, HubSpot, …) and the events each emits, connect an account, and manage the triggers that start an agent or assistant from those events.

devic integrations list --search gmail
devic integrations triggers gmail                 # an app's event types
devic integrations connect gmail --wait           # connect + build the tool server
devic triggers create --tool-server <id> --agent <id> --trigger GMAIL_NEW_GMAIL_MESSAGE
devic triggers events <id>

For detailed documentation, see integrations-and-triggers.md.


devic documents

Manage knowledge documents (markdown). List, read, create, update and manage versions.

devic documents list [--project <p>] [--folder <id>] [--file-type md]
devic documents get <documentId>
devic documents create --name <name> [content source] [--folder <id>] [--tags <t...>] [--as-skill]
devic documents update <documentId> [fields...] [--tags <t...>] [--as-skill|--no-skill]
devic documents versions list <documentId>
devic documents versions revert <documentId> <version>
devic documents attach <documentId> --target-type agent|assistant|environment --target-id <id>
devic documents usage <documentId>
devic documents folders create --name <name> [--as-skill] [--tags <t...>]

--folder on create, --tags and --as-skill/--no-skill require CLI ≥ 0.18.0. --as-skill publishes the document in the skills catalog; its name and description then come from the YAML frontmatter at the top of the markdown.

Providing document content (create / update)

The markdown body is never read from a shell redirect by itself. You must pass it through one of these content sources:

| Source | Example | |--------|---------| | Inline | devic documents update <id> --content "# Title\n..." | | From a file | devic documents update <id> --from-file SKILL.md | | From stdin (pipe) | cat SKILL.md \| devic documents update <id> --from-stdin |

⚠️ Do NOT use devic documents update <id> < file.md on its own. A bare shell redirect is only picked up when the process detects a piped stdin; the reliable forms are --from-file <path> or a real pipe with --from-stdin. To avoid silent mistakes, update now refuses an empty payload (it errors out instead of returning a misleading success), and the response includes versionCreated: true|false so you can confirm a new version was actually written. Always check that flag after updating.

update only creates a new version when the content actually changes; updating with identical content (or only metadata like --name/--folder) reports versionCreated: false.


devic skills

Browse and install Devic skills (documents or folders flagged as skills, in the SKILL.md format) into your local coding agents. Mirrors the skills.sh model: each skill is written as a folder named after the skill into the agent's skills directory, and an install registry (lockfile) tracks what is installed so it can be refreshed with update.

devic skills list (alias: ls)

List the skills catalog.

devic skills list [--tag <tag...>] [--search <text>] [--project <projectId>] [--limit <n>] [--page <n>]

| Option | Description | |--------|-------------| | --tag <tag...> | Filter by tag (repeatable, e.g. --tag cli --tag qa) | | --search <text> | Free-text search over name/description | | --project <projectId> | Filter by project id | | --limit <n> | Page size (max 200, default 100) — CLI ≥ 0.18.0 | | --page <n> | 1-based page number — CLI ≥ 0.18.0 |

Human output shows a table with id, name, type (document/folder), tags, and usage stats: linked agents, assistants, and reads (how often the skill was consulted by agents via the knowledge tools).

devic skills create

Create a folder-skill: the folder plus its SKILL.md manifest, with the name/description frontmatter already written. Requires CLI ≥ 0.18.0.

devic skills create <name> [-d <description>] [--tags <t...>] [--project <projectId>] [--parent <folderId>] [--from-file <path>]

| Option | Description | |--------|-------------| | -d, --description <text> | One-line description. It is what the model reads before deciding to load the skill, so phrase it as a trigger ("How to … when …") | | --tags <tags...> | Category tags | | --project <projectId> | Scope the skill to a project | | --parent <folderId> | Create it inside an existing folder | | --from-file <path> | Replace the generated stub manifest with your own markdown |

The output prints the folder id — that is what you attach as knowledgeSkills: [{ id, type: "folder" }] — and the manifest document id.

devic skills create "Incident triage" -d "How to triage a production incident." --tags ops
devic skills create "Release drill" --from-file ./SKILL.md

devic skills get

Show one catalog entry (by id or name), including its tags and usage counts. Requires CLI ≥ 0.18.0.

devic skills get <id|name>

devic skills tree

Show the files a skill contains — what an install would download — without recording an install. Requires CLI ≥ 0.18.0.

devic skills tree <id|name> [--out <dir>]

--out <dir> also writes the files to disk, preserving their relative paths. Useful to inspect or diff a skill before installing it.

devic skills tags

List the distinct tags across skills (for --tag filtering).

devic skills tags [--project <projectId>]

devic skills install (alias: add)

Download a skill's whole tree into your coding agents. The skill is resolved by id or name. Records the install locally (lockfile) and in Devic (install counter + per-user install date).

devic skills install <id|name> [-a <agents...>] [-g]

| Option | Description | |--------|-------------| | -a, --agent <agents...> | Target agents: claude-code, codex, cursor, opencode, cline, or * for all. Auto-detected from installed agents when omitted (falls back to claude-code) | | -g, --global | Install to the user-level (global) agent directories instead of the project ones |

Install locations (a folder named after the skill is created inside):

| Agent | Project path | Global path | |-------|--------------|-------------| | claude-code | .claude/skills/ | ~/.claude/skills/ | | codex | .agents/skills/ | ~/.codex/skills/ | | cursor | .agents/skills/ | ~/.cursor/skills/ | | opencode | .agents/skills/ | ~/.config/opencode/skills/ | | cline | .agents/skills/ | ~/.agents/skills/ |

A folder-skill is written with its full tree (SKILL.md + referenced files, at their relative paths). A document-skill is written as a single SKILL.md.

devic skills update

Refresh installed skills to their latest version. Compares the installed version (from the lockfile) against Devic and only rewrites what changed.

devic skills update [id|name] [-g]

Omit the argument to update every installed skill; pass an id/name to update just one. -g targets the global install registry.

devic skills installed

List what is installed locally (from the lockfile), with the installed version and date.

devic skills installed [-g]

The lockfile lives at .devic/skills.json (project) or ~/.devic/skills.json (global).


devic environments

The reusable package an agent runs on: the sandbox and its snapshot, the knowledge and tools connected entities inherit, and the encrypted variables they can read. Aliased as devic envs. Every command takes the environment by _id or by name.

devic environments list [--project <project>]
devic environments get <environment>
devic environments create --name <name> [--runtime node24|node22|python3.13]
                          [--init-script-file <file>] [--env KEY=VALUE ...]
                          [--snapshots] [--evolving-snapshot | --fixed-snapshot]
                          [--per-tenant-snapshots] [--auto-extend] [--persist]
devic environments update <environment> [same flags, each with a --no- form] [--unset-env KEY]
devic environments delete <environment>

devic environments connections <environment>
devic environments connect    <environment> agent|assistant <entityId>
devic environments disconnect <environment> agent|assistant <entityId>

devic environments snapshot init <environment>          # bake or re-bake the base
devic environments snapshot tenants <environment>
devic environments sessions list <environment>
devic environments clis <environment>

Two things that are easy to get wrong:

  • --env merges over the stored map, it does not replace it. Values are encrypted at rest and read back masked; the merge is what stops adding one variable from deleting the others. This is where a database password belongs — not in a thread message, which is stored and readable back indefinitely.
  • --snapshots alone gives a fixed snapshot. Sessions start from the saved state and cannot write back. --evolving-snapshot is what lets every session replace it. Fixed is the default because it stops a stray agent run from destroying a machine you spent time provisioning.

devic sandbox

A real Linux machine started on an environment. The lifecycle is explicit because none of it is free: starting provisions and bills a machine, and stopping is what saves the snapshot.

devic sandbox start  <environment> [--timeout <minutes>] [--tenant <id>] [--force]
devic sandbox status <environment>
devic sandbox exec   <environment> <command> [--cwd <path>] [--sudo]
devic sandbox stop   <environment> [--save | --no-save] [--force]

devic sandbox ls    <environment> [path]
devic sandbox cat   <environment> <path>
devic sandbox write <environment> <path> (--content <text> | --file <local> | --url <src>)

After start, the other commands find the live session on their own — no sandbox id to carry between calls.

exec takes a line of shell, not argv. Quote it: pipes, && and cd belong inside the command.

devic sandbox exec "Reporting box" 'cd /workspace && ./report.sh | tail -20'

Whether stop saves depends on the environment, matching the dashboard terminal: an evolving snapshot saves by default, a fixed one does not unless you pass --save. Saying nothing never overwrites a snapshot someone froze on purpose — and --save on a fixed environment is how you provision one.

For the provisioning workflow (install once, bake, then leave it alone), the snapshot modes, per-tenant snapshots, the SESSION_ACTIVE and SNAPSHOT_SAVE_IN_PROGRESS conflicts and cleanup, see environments-and-sandboxes.md.


devic snippets

Code snippets: functions written once and given to agents and assistants as tools. Alias: devic code-snippets. Each defines main(input); parameters is the JSON Schema of input, and the model calls it by its toolName. The full contract (how the code runs, versions, validation) is in the devic-api skill, code-snippets.md.

devic snippets list   [--search <text>] [--language <lang>] [--enabled | --disabled]
                      [--tag <tag>] [--project <id>] [--limit <n>] [--offset <n>]
devic snippets get    <id> [--code-only]
devic snippets create --name <snake_case> --description <text>
                      (--code-file <file> | --code <source>) --parameters <json|file>
                      [--language <lang>] [--tags <a,b>] [--project <id>] [--disabled]
                      [--from-json <file>]
devic snippets update <id> [--name ...] [--description ...] [--code-file ... | --code ...]
                      [--parameters <json|file>] [--language <lang>] [--tags <a,b>]
                      [--enabled | --disabled] [--test-cases <json|file>]
                      [--expected-version <n>] [--from-json <file>]
devic snippets delete <id>
devic snippets test   <id> [--input <json|file>]... [--inputs-file <file>] [--timeout <ms>]

| Option | Description | |--------|-------------| | --code-file <file> | Source defining main(input); - reads stdin. The language is inferred from .js, .mjs, .cjs, .ts or .py unless --language is given | | --parameters <json\|file> | JSON Schema of input (type: "object"), inline or a file | | --from-json <file> | Full payload: name, description, language, code, parameters, tags, testCases, enabled, projectId. Flags override it | | --test-cases <json\|file> | Replaces the saved test cases: [{ "name": "...", "input": { ... } }] | | --expected-version <n> | Refuse with 409 when the snippet is no longer at version n | | --code-only | get prints only the source, to redirect into a file | | --input <json\|file> | One input object; repeat it for several runs. Without inputs, the saved test cases run |

update changes only what you pass. A change to the name, description, language, code or parameters saves a new version; --enabled/--disabled, tags and test cases do not.

test exits 1 when any run fails, so it works as a check in a script. A failing run is still printed with its error: an input that does not match parameters fails before the code runs, with the schema error.

# Round trip: edit the code locally and check it before agents get it
devic snippets get 69cd0438... --code-only > distance.js
$EDITOR distance.js
devic snippets update 69cd0438... --code-file distance.js --expected-version 2
devic snippets test 69cd0438... && echo "ready"

To give a snippet to an agent, add its id to assistantSpecialization.codeSnippetIds (assistants: top-level codeSnippetIds) with --from-json. If the entity has an enabledTools list, add the snippet's toolName to it too (for example with --enabled-tools on assistants), or runs leave the snippet out.


devic feedback

Submit and view feedback on chat messages and thread messages.

devic feedback submit-chat

devic feedback submit-chat <identifier> <chatUid> --message-id <id> [options]

| Option | Description | |--------|-------------| | --message-id <id> | Required. Message UID to give feedback on | | --positive | Positive feedback (thumbs up) | | --negative | Negative feedback (thumbs down) | | --comment <text> | Feedback comment | | --from-json <file> | Read full feedback payload from file |

devic feedback list-chat

devic feedback list-chat <identifier> <chatUid>

devic feedback submit-thread

devic feedback submit-thread <threadId> --message-id <id> [options]

Same options as submit-chat.

devic feedback list-thread

devic feedback list-thread <threadId>

Usage Examples

Send a message and get the result

# Simple chat
devic assistants chat default -m "What is the capital of France?"

# Continue a conversation
CHAT_UID=$(devic assistants chat default -m "Hello" -o json | jq -r '.chatUID')
devic assistants chat default -m "Tell me more" --chat-uid "$CHAT_UID"

Create an agent and run a thread

# Create agent from JSON
cat <<'EOF' | devic agents create --from-json -
{
  "name": "Data Analyst",
  "description": "Analyzes data and creates reports",
  "assistantSpecialization": {
    "presets": "You are a data analyst. Analyze data and provide insights.",
    "model": "gpt-4o"
  }
}
EOF

# Run a thread and wait for completion
devic agents threads create <agentId> -m "Analyze Q4 sales data" --wait

Handle thread approvals in a script

# Create thread
RESULT=$(devic agents threads create <agentId> -m "Delete old records" --wait -o json)
STATE=$(echo "$RESULT" | jq -r '.state')

if [ "$STATE" = "paused_for_approval" ]; then
  THREAD_ID=$(echo "$RESULT" | jq -r '._id')
  devic agents threads approve "$THREAD_ID" -m "Approved"
fi

Set up a tool server with tools

# Create tool server
cat <<'EOF' | devic tool-servers create --from-json -
{
  "name": "Weather API",
  "url": "https://api.weather.example.com",
  "toolDefinitions": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get weather for a city",
      "parameters": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    },
    "endpoint": "/weather/${city}",
    "method": "GET",
    "pathParametersKeys": ["city"]
  }]
}
EOF

# Test the tool
echo '{"city":"London"}' | devic tool-servers tools test <serverId> get_weather --from-json -

Search for specific data in agent threads

# List threads without content (fast, metadata only)
devic agents threads list <agentId> --omit-content --limit 50

# Find a specific email within a thread's content
devic agents threads get <threadId> --grep "user@example.com"

# Combine: list threads, then search each for a keyword
devic agents threads list <agentId> --omit-content -o json | \
  jq -r '.[].threadId' | \
  while read tid; do
    RESULT=$(devic agents threads get "$tid" --grep "target@email.com" -o json)
    COUNT=$(echo "$RESULT" | jq '.threadContent | length')
    if [ "$COUNT" -gt "0" ]; then echo "Found in thread: $tid"; fi
  done

A nightly report against a private database

The shape all of this exists for: no external scheduler, and no credential in a message.

# 1. An environment holding the credentials and the tooling.
devic environments create --name "Reporting box" \
  --runtime python3.13 \
  --env "DATABASE_URL=postgres://reporter:...@db.internal/analytics" \
  --env "SSH_PRIVATE_KEY=$(cat ~/.ssh/reporter_ed25519)" \
  --init-script "pip install psycopg2-binary && mkdir -p /workspace" \
  --snapshots --auto-extend

# 2. Bake the snapshot once, so no run pays for the install.
devic environments snapshot init "Reporting box"

# 3. Try it by hand before trusting it to a schedule.
devic sandbox start "Reporting box" --timeout 10
devic sandbox write "Reporting box" /workspace/report.py --file ./report.py
devic sandbox exec "Reporting box" 'cd /workspace && python report.py'
devic sandbox stop "Reporting box"          # saves into the snapshot

# 4. An agent that wakes itself at 07:00 on weekdays, on that machine.
devic agents create --name "Nightly analytics report" \
  --environment "Reporting box" \
  --cron "0 7 * * 1-5" --timezone Europe/Madrid

Step 3 is the one worth keeping: a sandbox you drive by hand is the same machine the agent will get, so a script verified there is verified for real.

Pipe JSON between commands

# Get all completed threads and their evaluations
devic agents threads list <agentId> --state COMPLETED -o json | \
  jq -r '.[].threadId' | \
  while read tid; do devic agents threads evaluate "$tid"; done