Egnyte Agentic CLI

The Egnyte Agentic CLI (@egnyte/agentic-cli) is a command-line tool for interacting with the Egnyte API from terminals, CI pipelines, and AI coding agents. It produces JSON-only output, exposes self-describing schema introspection, and enforces --dry-run confirmation before any mutating operation.

Note: This tool is designed for two primary audiences: AI agents (Claude Code, Cursor, Copilot Workspace) that need structured, machine-readable output; and developers automating Egnyte workflows from scripts or CI pipelines.


Installation

Requirements: Node.js ≥ 14, npm ≥ 6

npm install -g @egnyte/agentic-cli

To upgrade to the latest version:

npm install -g @egnyte/agentic-cli@latest

Verify the installation:

egnyte --help
egnyte schema --list

Authentication

The CLI ships with a built-in OAuth app — no app registration required. Only --domain is needed:

egnyte login --domain https://mycompany.egnyte.com

The CLI opens your browser for OAuth authorization. After approving, copy the code= value from the redirect URL and paste it in the terminal. The access token is stored at ~/.config/egnyte-cli/config.json with file mode 0600 (owner read/write only).

Using your own OAuth app (optional — for custom redirect URIs, restricted scopes, or enterprise app policies):

egnyte login \
  --domain https://mycompany.egnyte.com \
  --client-id YOUR_CLIENT_ID \
  --client-secret YOUR_CLIENT_SECRET

# Equivalent via env vars
EGNYTE_DOMAIN=https://mycompany.egnyte.com \
EGNYTE_CLIENT_ID=YOUR_CLIENT_ID \
EGNYTE_CLIENT_SECRET=YOUR_CLIENT_SECRET \
egnyte login

Register your own app at developers.egnyte.com if needed.

Option B — Environment Variables (CI / Headless / AI Agents)

Set EGNYTE_TOKEN and EGNYTE_DOMAIN to bypass stored credentials entirely:

export EGNYTE_TOKEN=<bearer-token>
export EGNYTE_DOMAIN=https://mycompany.egnyte.com

Additional login-time variables:

VariableDescription
EGNYTE_CLIENT_IDOAuth application client ID
EGNYTE_CLIENT_SECRETOAuth application client secret
EGNYTE_SCOPESpace-separated scopes (omit for all scopes)
EGNYTE_REDIRECT_URIRedirect URI (default: https://www.egnyte.com)

Auth Precedence

--token / --domain flags
        ↓
EGNYTE_TOKEN / EGNYTE_DOMAIN env vars
        ↓
stored profile (~/.config/egnyte-cli/config.json)

Named Profiles

Use named profiles to manage multiple domains:

# Log in to staging and production as separate profiles
egnyte login --domain https://mycompany-staging.egnyte.com --client-id <id> --client-secret <s> --profile staging
egnyte login --domain https://mycompany.egnyte.com --client-id <id> --client-secret <s> --profile prod

# Restrict scopes for a profile
egnyte login --domain https://mycompany.egnyte.com --client-id <id> --client-secret <s> \
  --scope "Egnyte.filesystem Egnyte.user" --profile prod-readonly

# Switch the active profile
egnyte profiles use staging

# List all profiles
egnyte profiles list

# Remove a profile
egnyte profiles remove staging

Token Auto-Refresh

If a stored token is within 5 minutes of expiry and a refresh_token is present, the CLI silently refreshes it before executing the request.


Global Flags

Every command accepts the following flags:

FlagDescription
--json '{}'JSON request body or query parameters
--fields a,b,cReturn only specified fields (reduces response size and AI token cost)
--dry-runPrint the equivalent curl command; do not execute
--yesExecute a mutating command without a confirmation prompt
--bulk-file-path <csv>Execute a command in bulk from a CSV file
--from-csv <csv>Alias for --bulk-file-path
--parallelism <n>Max concurrent bulk operations (default: 2)
--progressHuman-readable progress updates on stderr
--json-progressNewline-delimited JSON progress events on stderr
--profile <name>Use a named profile instead of the default
--token / --domainOverride stored credentials for a single call

Schema Discovery

Inspect available operations and their parameters at runtime — no external documentation required.

# List all available operations
egnyte schema --list

# Full parameter reference for a specific operation
egnyte schema fs.action
egnyte schema users.create
egnyte schema search.advanced
egnyte schema ai.ask-kb

Example output:

{
  "summary": "Perform a file/folder operation: add_folder | move | copy | rename",
  "method": "POST",
  "endpoint": "/pubapi/v1/fs/{path}",
  "mutating": true,
  "body_params": {
    "action":      { "type": "string", "required": true,  "description": "add_folder | move | copy | rename" },
    "destination": { "type": "string", "required": false, "description": "Destination path (required for move and copy)" },
    "new_name":    { "type": "string", "required": false, "description": "New filename (required for rename)" }
  },
  "example": "egnyte fs action /Shared/NewFolder --json '{\"action\": \"add_folder\"}' --dry-run"
}

Commands

All commands write JSON to stdout. Errors are written as {"error":"..."} to stderr. Exit code is 0 on success, 1 on error.


File System

Get File or Folder Metadata

egnyte fs get <path> [--json '{}'] [--fields a,b,c]
ParameterTypeRequiredDescription
pathstringYesAbsolute path to the file or folder (must start with /)
list_contentbooleanNoSet true to list folder contents
countintegerNoItems per page (for pagination)
offsetintegerNoPagination offset

Example — file metadata:

egnyte fs get /Shared/report.pdf --fields name,path,size,entry_id

Example — list folder contents with pagination:

egnyte fs get /Shared \
  --json '{"list_content": true, "count": 50, "offset": 0}' \
  --fields files.name,files.path,files.size,folders.name,folders.path,folders.is_folder

Folder and File Actions

Named subcommands (preferred):

# Create a folder
egnyte fs mkdir /Shared/NewFolder --dry-run
egnyte fs mkdir /Shared/NewFolder --yes

# Rename a file or folder
egnyte fs rename /Shared/report.pdf --name report-final.pdf --yes

# Move a file or folder
egnyte fs move /Shared/report.pdf --to /Shared/Archive/report.pdf --yes

# Copy a file or folder
egnyte fs copy /Shared/report.pdf --to /Shared/Backup/report.pdf --yes

Always run with --dry-run first to preview the equivalent curl command before executing.

Upload

# Standard upload — recommended for files ≤ 10 MB
egnyte fs upload /Shared/docs/report.pdf --file ./report.pdf --yes

# Chunked upload — recommended for files > 10 MB
# Minimum chunk size enforced by the API: 10 MB (except for the final chunk)
egnyte fs upload-chunked /Shared/bigfile.iso --file ./bigfile.iso --yes

# Custom chunk size in bytes (must be ≥ 10485760 for non-final chunks)
egnyte fs upload-chunked /Shared/bigfile.iso --file ./bigfile.iso --chunk-size 15728640 --yes

Files smaller than one chunk size automatically fall back to the standard /fs-content endpoint.

Download

Downloads stream directly to disk and are safe for files of any size.

# Download by path
egnyte fs download /Shared/report.pdf --out ./report.pdf

# Download by group_id
egnyte fs download-by-id <group-id> --out ./report.pdf

# Resume an interrupted download
egnyte fs download-by-id <group-id> --out ./report.pdf --resume

Delete

egnyte fs delete /Shared/old-report.pdf --dry-run
egnyte fs delete /Shared/old-report.pdf --yes

Read File Content as Text

Fetch file content as text without writing to disk — useful for reading documents or CSVs in an agent workflow.

egnyte fs get-content /Shared/notes.txt
egnyte fs get-content /Shared/data.csv --json '{"offset":0,"limit":10000}'

Custom Metadata

# List all metadata namespaces and their field definitions
egnyte fs list-metadata-namespaces --fields namespace,fields

# Set custom metadata on a file
egnyte fs set-metadata /Shared/contract.pdf \
  --json '{"namespace":"contract","values":{"status":"signed","reviewed_by":"jsmith"}}' \
  --dry-run

egnyte fs set-metadata /Shared/contract.pdf \
  --json '{"namespace":"contract","values":{"status":"signed","reviewed_by":"jsmith"}}' \
  --yes

Basic Full-Text Search

egnyte search <query> [--json '{}'] [--fields a,b,c]
# Basic search
egnyte search "quarterly report" --fields results.name,results.path,results.size

# Scoped to a folder with pagination
egnyte search "budget" \
  --json '{"count": 20, "offset": 0, "folder": "/Shared/Finance", "type": "file"}' \
  --fields results.name,results.path,results.size

Advanced Search

egnyte search advanced <query> [--json '{}'] [--fields a,b,c]
# Filter by modification date
egnyte search advanced "contract" \
  --json '{"folder":"/Shared/Legal","modified_after":"2024-01-01","type":"file"}' \
  --fields results.name,results.path,results.size

# Filter by custom metadata
egnyte search advanced "NDA" \
  --json '{"custom_metadata":{"status":"signed"},"namespaces":["contract"]}' \
  --fields results.name,results.path

AI

All AI commands are read-only — --yes is not required.

Ask a Question

egnyte ai ask "<question>" [--json '{}'] [--fields a,b,c]
# General Copilot question
egnyte ai ask "What are the key metrics in Q3?" --fields response

# Scoped to specific folders with citations
egnyte ai ask "Revenue trends?" \
  --json '{"selectedItems":{"folders":[{"id":"<folder-id>"}]},"includeCitations":true}' \
  --fields response,citations

Ask About a Specific File

Path is automatically resolved to an entry-id.

egnyte ai ask-document /Shared/Contracts/acme.pdf "What are the payment terms?" --fields response
egnyte ai ask-document /Shared/Contracts/acme.pdf "What are the payment terms?" \
  --json '{"includeCitations":true}' --fields response,citations

Summarize a File

egnyte ai summarize /Shared/Reports/annual-report.pdf --fields response

Knowledge Bases

# List active Knowledge Bases
egnyte ai list-kbs \
  --json '{"status":["ACTIVE"],"sortBy":["name"],"sortDirection":["ASC"]}' \
  --fields content

# Query a Knowledge Base with citations
egnyte ai ask-kb kb-abc123 "What is the refund policy?" \
  --json '{"includeCitations":true}' \
  --fields response,citations

Hybrid Search

Combines semantic and keyword matching. semanticWeight ranges from 0.0 (pure keyword) to 1.0 (pure semantic).

egnyte ai hybrid-search "quarterly report" \
  --json '{"semanticWeight":0.7,"folderPath":"/Shared/Finance","limit":10}' \
  --fields results

Agents

Use agents instead of ai ask when you need multi-turn conversation continuity, custom instructions, or complex workflows.

agents ask is synchronous by default — it polls until the agent responds (up to 5 minutes). Use --no-wait to fire-and-forget.

All agent commands are read-only — --yes is not required.

Response status values: PENDING | RUNNING | COMPLETED | FAILED

# List all available agents
egnyte agents list --fields agentId,name,status,category

# Ask an agent — blocks until complete
egnyte agents ask <agentId> "Summarize the Q3 results" --fields responseText,citations

# Multi-turn: continue a prior conversation
egnyte agents ask <agentId> "Now compare that to Q2" \
  --json '{"conversationId":"<id from prior response>"}' \
  --fields responseText

# Custom agent behavior for this call
egnyte agents ask <agentId> "Draft a summary" \
  --json '{"instructions":"Respond in bullet points, no more than 5 items"}' \
  --fields responseText

# Scope to specific files
egnyte agents ask <agentId> "What are the key risks?" \
  --json '{"selectedItems":{"files":[{"entryId":"<id>","filePath":"/Shared/contract.pdf"}]}}' \
  --fields responseText,citations

# Fire-and-forget — returns requestId and conversationId immediately
egnyte agents ask <agentId> "Long running analysis task" \
  --no-wait --fields requestId,conversationId

# Check status later
egnyte agents status <agentId> <requestId> --fields status,responseText,citations

accessibility values: anyone | domain | password | recipients

# Create a shared link
egnyte links create \
  --json '{"path":"/Shared/report.pdf","type":"file","accessibility":"domain"}' \
  --dry-run

egnyte links create \
  --json '{"path":"/Shared/report.pdf","type":"file","accessibility":"anyone","expiry_date":"2026-12-31"}' \
  --fields id,url,path,accessibility --yes

# List links for a path
egnyte links list --json '{"path":"/Shared/report.pdf"}' --fields ids,total_count

# Get a specific link
egnyte links get <link-id> --fields id,url,path,accessibility

# Delete a link
egnyte links delete <link-id> --dry-run
egnyte links delete <link-id> --yes

Users

Uses the SCIM-compatible /pubapi/v2/users endpoint.

# List users
egnyte users list --json '{"count": 50}' --fields resources.id,resources.userName,resources.email,resources.active

# Get a user by numeric ID
egnyte users get <id> --fields userName,email,active,userType

# Create a user
egnyte users create \
  --json '{"userName":"jsmith@co.com","email":{"value":"jsmith@co.com"},"active":true,"sendInvite":false}' \
  --dry-run
egnyte users create \
  --json '{"userName":"jsmith@co.com","email":{"value":"jsmith@co.com"},"active":true,"sendInvite":false}' \
  --yes

# Update a user — PATCH, only fields you include are changed
egnyte users update <id> --json '{"active": false}' --yes

# Delete a user
egnyte users delete <id> --dry-run
egnyte users delete <id> --yes

Groups

Uses the SCIM-compatible /pubapi/v2/groups endpoint.

# List groups
egnyte groups list --json '{"count": 50}' --fields resources.id,resources.displayName

# Get a group by ID
egnyte groups get <id> --fields displayName,members

# Create a group
egnyte groups create --json '{"displayName":"Engineering"}' --yes

# Update a group
egnyte groups update <id> --json '{"displayName":"Eng Team"}' --yes

# Delete a group
egnyte groups delete <id> --dry-run
egnyte groups delete <id> --yes

Permissions

Folder permission levels: Owner | Editor | Viewer | None

# Get user permissions on a folder
egnyte perms get-user /Shared/Finance --fields users

# Set user permissions
egnyte perms set-user /Shared/Finance \
  --json '{"users": {"jsmith": "Viewer", "mjones": "Editor"}}' \
  --yes

# Remove user permissions
egnyte perms delete-user /Shared/Finance --json '{"users": ["jsmith"]}' --yes

# Get group permissions on a folder
egnyte perms get-group /Shared/Finance --fields groups

# Set group permissions
egnyte perms set-group /Shared/Finance \
  --json '{"groups": {"Engineering": "Editor"}}' \
  --yes

# Remove group permissions
egnyte perms delete-group /Shared/Finance --json '{"groups": ["Engineering"]}' --yes

# Get a specific user's permission level
egnyte perms get-by-user jsmith --json '{"folder": "/Shared/Finance"}'

Events

Pull the audit trail for file and folder activity across the domain.

Event types: create | move | delete | edit | lock | unlock | restore

# Step 1 — get the latest event ID (your polling start point)
egnyte events get-cursor

# Step 2 — list events from that ID
egnyte events list --json '{"id": 12345678, "count": 20}' \
  --fields events.id,events.action,events.actor,events.timestamp

# Filter by folder and event type
egnyte events list \
  --json '{"id": 12345678, "count": 50, "folder": "/Shared", "type": "create|move|delete"}' \
  --fields events.id,events.action,events.actor,events.timestamp,events.data

Notes

Comments attached to files.

# Add a note
egnyte notes add /Shared/report.pdf \
  --json '{"body": "Please review section 3 before the Thursday call"}' \
  --dry-run
egnyte notes add /Shared/report.pdf \
  --json '{"body": "Please review section 3 before the Thursday call"}'

# List all notes on a file
egnyte notes list /Shared/report.pdf

# Get a note by ID
egnyte notes get <note-id>

# Delete a note
egnyte notes delete <note-id> --dry-run
egnyte notes delete <note-id>

File Locking

Prevent concurrent edits by locking a file.

# Lock a file
egnyte lock lock /Shared/report.pdf \
  --json '{"lock_token": "my-token", "lock_timeout": 300}'

# Check lock status
egnyte lock get /Shared/report.pdf --fields locked,lock_owner,lock_timeout

# Unlock a file
egnyte lock unlock /Shared/report.pdf --json '{"lock_token": "my-token"}'

Trash

# List items in the trash
egnyte trash list --json '{"count": 50}' --fields items.name,items.path,items.size,items.id

# Restore items (requires IDs from trash list)
egnyte trash restore --json '{"ids": ["item_id_1", "item_id_2"]}' --dry-run
egnyte trash restore --json '{"ids": ["item_id_1"]}' --yes

# Permanently delete items from the trash
egnyte trash delete --json '{"ids": ["item_id_1"]}' --dry-run
egnyte trash delete --json '{"ids": ["item_id_1"]}' --yes

Projects

Manage lifecycle and metadata for project folders.

# List all project folders
egnyte projects list --fields name,id,status

# Get details for a specific project
egnyte projects get <project-id> --fields name,status

# Create a project from a template (v2 API)
egnyte projects create \
  --json '{"name":"New HQ","status":"pending","parentFolderId":"...","templateFolderId":"...","folderName":"New HQ Folder"}' \
  --yes

# Mark an existing folder as a project (v1 API)
egnyte projects create \
  --json '{"name":"Existing Site","status":"in-progress","rootFolderId":"..."}' \
  --yes

# Update project metadata
egnyte projects update <project-id> --json '{"status": "completed"}' --yes

# Delete project metadata (reverts the folder to normal)
egnyte projects delete <project-id> --yes

User Info

Returns the authenticated user's live profile from the API, validating that the stored token is still accepted.

egnyte userinfo
egnyte userinfo --fields username,email,user_type

Note: Unlike egnyte whoami (which reads stored credential metadata without a network call), userinfo makes a live API request to verify the token.


Bulk Operations

For supported mutating commands, pass a CSV file to run one API call per row.

# delete.csv format:
# path
# /Shared/old-a.pdf
# /Shared/old-b.pdf

# Preview first
egnyte fs delete --bulk-file-path ./delete.csv --dry-run

# Execute with concurrency and progress reporting
egnyte fs delete --bulk-file-path ./delete.csv --parallelism 4 --progress --yes

Upload in bulk:

egnyte fs upload --bulk-file-path ./uploads.csv --parallelism 4 --progress --yes

Rate Limiting and Retries

On 429 Too Many Requests, the CLI retries up to 3 times, honoring the Retry-After header when present and falling back to exponential backoff (1 s → 2 s → 4 s).

On 5xx server errors, the CLI retries GET and HEAD requests up to 3 times with exponential backoff and jitter. POST, PUT, and DELETE requests fail immediately to avoid duplicate mutations.


Using with Claude Code

Setup

npm install -g @egnyte/agentic-cli

# Authenticate — built-in OAuth app, no registration needed
egnyte login --domain https://mycompany.egnyte.com

# Add the skill file to Claude's global instructions
cat CLAUDE.md >> ~/.claude/CLAUDE.md

The CLAUDE.md file teaches Claude the core operating rules: always --dry-run before mutations, always --fields on list calls, never guess file IDs, and paths must start with /.

Example Prompts

Read operations — Claude executes immediately:

What files are in /Shared/Finance?
How many PDFs are in /Shared/Projects? Show names and sizes only.
Search for "quarterly report" in /Shared.

Mutations — Claude dry-runs, shows the curl output, then asks for confirmation:

Create a folder called /Shared/2026-Archive.
Upload ./report.pdf to /Shared/Finance.
Move everything in /Shared/Inbox to /Shared/Processed.
Delete /Shared/Temp.

Multi-step tasks:

Create /Shared/Archive/Q1-2026, upload all PDFs from ./exports into it, then list what was uploaded.

Dry-Run Confirmation Flow

When you ask Claude to delete a file, it will:

  1. Run egnyte fs delete /Shared/report.pdf --dry-run
  2. Display the exact curl command with the token masked as ***
  3. Ask for your confirmation before executing
curl -X DELETE 'https://mycompany.egnyte.com/pubapi/v1/fs/Shared/report.pdf' \
  -H 'Authorization: ***'

Ready to delete /Shared/report.pdf. Confirm?

Security

Credential Storage

Tokens are stored at ~/.config/egnyte-cli/config.json with file mode 0600 (owner read/write only). The file contains: access_token, refresh_token, client_id, client_secret, domain, and expires_at.

To remove stored credentials:

egnyte logout                          # removes the default profile
egnyte profiles remove <name>          # removes a named profile
rm ~/.config/egnyte-cli/config.json    # removes all profiles

Path Validation

Every path argument is validated against the following rules before any network call:

RuleExample Blocked Input
Must start with /Shared/docs
No path traversal (..)/Shared/../../etc/passwd
No pre-encoded slash/Shared%2Fdocs
No double-encoded characters/Shared%252F
No embedded query string/Shared?foo=bar
No null byte/Shared%00docs
$ egnyte fs get /Shared/../../etc/passwd
{"error":"Invalid path — path traversal (..) detected"}

Error Codes

StatusDescriptionResolution
400Malformed request body or invalid parameter valueCheck the parameter table for the command using egnyte schema <operation>
401Invalid or expired tokenRun egnyte login to re-authenticate, or check that EGNYTE_TOKEN is set correctly
403Insufficient permissionsVerify the authenticated user has the required folder access
404File, folder, or resource not foundConfirm the path exists with egnyte fs get <path>
429Too many requestsThe CLI retries automatically; if persistent, reduce --parallelism in bulk operations
5xxServer errorGET/HEAD requests retry automatically; POST/PUT/DELETE fail immediately