Documentation
Zoho Expense CLI
zoho-expense-pp-cli
Install
npx -y @mvanhorn/printing-press-library install zoho-expenseInstalls the CLI binary and the agent skill for Claude Code, Codex, Cursor, Gemini CLI, Copilot, and more. Add --cli-only or --skill-only for one half. Requires Node.
Documentation
Upload receipts, auto-tag expenses from learned merchant memory, and close out the month in one command — the agent-friendly Zoho Expense CLI nobody else has built.
Zoho ships autoscan; this CLI ships everything that comes after — receipt dedup, learned merchant→category mapping, India GST splitting, and monthly report bundling. Built for AI agents that ingest invoices from an inbox on a cadence and need an idempotent, structured-output upload path. India-region by default; works against any Zoho datacenter by overriding the base URL.
Learn more at Zoho Expense.
Created by @amitav13 (Amitav Khandelwal).
Authentication
Zoho Expense uses OAuth 2.0 with a self-client authorization-code flow. Create a self-client at https://api-console.zoho.in/, generate a 10-minute authorization code, then run zoho-expense-pp-cli auth login --client-id <id> --client-secret <secret> (browser callback flow). The CLI exchanges the code for a long-lived refresh token (stored at ~/.config/zoho-expense-pp-cli/config.toml) and refreshes access tokens transparently. Tokens use the literal Zoho-oauthtoken Authorization prefix (not Bearer).
Quick Start
# Exchange the 10-min code for a refresh token
zoho-expense-pp-cli auth login --client-id $ZOHO_EXPENSE_CLIENT_ID --client-secret $ZOHO_EXPENSE_CLIENT_SECRET
# Set the active organization (first one in the list)
zoho-expense-pp-cli org use $(zoho-expense-pp-cli organizations list --json | jq -r '.[0].organization_id')
# Sync categories, reporting tags, projects, customers, expenses into the local store
zoho-expense-pp-cli sync
# Upload one receipt with hash-dedup and auto-tag
zoho-expense-pp-cli receipt upload ~/Downloads/uber-receipt.pdf --auto-tag
# Batch ingest a folder for AI consumption
zoho-expense-pp-cli invoice ingest ~/Downloads/october-invoices --auto-tag --agent
# Bundle the month into a report and submit
zoho-expense-pp-cli close --month 2026-10 --auto-submit
Unique Features
These capabilities aren't available in any other tool for this API.
Hermes-first ingestion
-
invoice ingest— Batch upload a folder of invoices to Zoho Expense, SHA256-dedup against the local store, poll autoscan in parallel, and auto-tag from learned merchant memory — the headline workflow for AI agents that ingest invoices from email.Agents that ingest invoices on a cadence need one command that handles dedup, autoscan, and tagging — not three.
zoho-expense-pp-cli invoice ingest ~/Downloads/october-invoices --auto-tag --agent -
receipt upload— SHA256-hash incoming receipts at upload, refuse duplicates (use --force to override).Idempotent receipt upload — safe to re-run on a folder agents have already processed.
zoho-expense-pp-cli receipt upload ~/Downloads/uber-receipt.pdf --auto-tag -
expense-untagged— List expenses missing category_id or project_id;--auto-fixapplies the merchant→tag memory map and PUTs back to Zoho.One command turns 30 untagged scanned receipts into 30 tagged ones, using the user's own historical mapping.
zoho-expense-pp-cli expense-untagged --auto-fix --agent
Local intelligence
-
merchant list— Build a local merchant→category/project/tag mapping from sync history; pre-fill tags on first sight of a new merchant.Solves Zoho's 'first-time merchant is always uncategorized' limitation with zero API calls.
zoho-expense-pp-cli merchant list --agent --select merchant_name,category_name,seen_count -
merchant map— Train the local merchant→category mapping for future auto-tag (one merchant at a time, or bulk from a CSV).Lets the user (or agent) explicitly seed the auto-tag map without waiting for sync history to learn it.
zoho-expense-pp-cli merchant map 'AWS' --category Software --project Engineering
Monthly automation
-
close— Bundle a month's unreported expenses into an expense report in one shot — flags still-processing autoscans, flags untagged items, creates the report, attaches, optionally submits.The natural end of every monthly agent workflow — turn raw expenses into a submitted report without leaving the terminal.
zoho-expense-pp-cli close --month 2026-10 --auto-submit --agent
India tax workflow
-
gst-split— India-specific: parse an expense's tax_id and line items, compute CGST/SGST (intra-state) or IGST (inter-state) shares, emit the breakdown or update the expense.Turns a monthly export into something a CA can directly file, without the user re-doing the math by hand.
zoho-expense-pp-cli gst-split exp_1234567890 --emit-csv
Usage
Run zoho-expense-pp-cli --help for the full command reference and flag list.
Commands
currencies
Currencies and exchange rates
zoho-expense-pp-cli currencies create- Add a currency to the orgzoho-expense-pp-cli currencies delete- Delete a currencyzoho-expense-pp-cli currencies get- Get a currencyzoho-expense-pp-cli currencies list- List currencies configured in the orgzoho-expense-pp-cli currencies update- Update a currency
customers
Customers (contacts) expenses can be billed to
zoho-expense-pp-cli customers create- Create a customerzoho-expense-pp-cli customers delete- Delete a customerzoho-expense-pp-cli customers get- Get a customerzoho-expense-pp-cli customers list- List customerszoho-expense-pp-cli customers update- Update a customer
expense_categories
Expense categories used to classify expenses
zoho-expense-pp-cli expense_categories create- Create an expense categoryzoho-expense-pp-cli expense_categories delete- Delete an expense categoryzoho-expense-pp-cli expense_categories disable- Disable a categoryzoho-expense-pp-cli expense_categories enable- Enable a categoryzoho-expense-pp-cli expense_categories get- Get an expense categoryzoho-expense-pp-cli expense_categories list- List expense categorieszoho-expense-pp-cli expense_categories update- Update an expense category
expense_reports
Expense reports — bundles of expenses submitted for approval and reimbursement
zoho-expense-pp-cli expense_reports approval-history- View the approval history of a reportzoho-expense-pp-cli expense_reports approve- Approve an expense reportzoho-expense-pp-cli expense_reports create- Create an expense reportzoho-expense-pp-cli expense_reports get- Get an expense report (includes attached expenses)zoho-expense-pp-cli expense_reports list- List expense reportszoho-expense-pp-cli expense_reports reimburse- Mark an expense report as reimbursedzoho-expense-pp-cli expense_reports reject- Reject an expense reportzoho-expense-pp-cli expense_reports update- Update an expense report — used to attach more expenses
expenses
Expenses — the primary entity for an expense management CLI
zoho-expense-pp-cli expenses create- Create an expense (JSON body — for receipt upload use 'receipt upload')zoho-expense-pp-cli expenses get- Get a single expensezoho-expense-pp-cli expenses list- List expenses with rich filters (date range, status, user, category, project)zoho-expense-pp-cli expenses merge- Merge multiple expenses (used to dedupe a scanned-receipt expense with a manual one)zoho-expense-pp-cli expenses update- Update an expense — used to add category/project/tags after autoscan
organizations
Zoho Expense organizations you have access to
zoho-expense-pp-cli organizations get- Get organization detailszoho-expense-pp-cli organizations list- List organizations accessible to the authenticated user
projects
Projects expenses can be associated with
zoho-expense-pp-cli projects activate- Mark a project activezoho-expense-pp-cli projects create- Create a projectzoho-expense-pp-cli projects deactivate- Mark a project inactivezoho-expense-pp-cli projects delete- Delete a projectzoho-expense-pp-cli projects get- Get a projectzoho-expense-pp-cli projects list- List projectszoho-expense-pp-cli projects update- Update a project
receipts
Upload receipts for autoscan
zoho-expense-pp-cli receipts- Upload a receipt file (multipart). Server queues for autoscan; pollexpenses getfor status.
reporting_tags
Reporting tags (custom tagging schema for expenses, e.g. cost center, billable, GST treatment)
zoho-expense-pp-cli reporting_tags activate- Activate a reporting tagzoho-expense-pp-cli reporting_tags create- Create a reporting tagzoho-expense-pp-cli reporting_tags deactivate- Deactivate a reporting tagzoho-expense-pp-cli reporting_tags delete- Delete a reporting tagzoho-expense-pp-cli reporting_tags get- Get a reporting tagzoho-expense-pp-cli reporting_tags list- List reporting tagszoho-expense-pp-cli reporting_tags list-options- List all options for a reporting tagzoho-expense-pp-cli reporting_tags update- Update a reporting tag
taxes
Taxes (GST in India) applied to expenses
zoho-expense-pp-cli taxes create- Create a taxzoho-expense-pp-cli taxes delete- Delete a taxzoho-expense-pp-cli taxes get- Get a taxzoho-expense-pp-cli taxes list- List taxeszoho-expense-pp-cli taxes update- Update a tax
trips
Business trips that group related expenses
zoho-expense-pp-cli trips approve- Approve a tripzoho-expense-pp-cli trips cancel- Cancel a tripzoho-expense-pp-cli trips close- Close a trip (no further expenses can be added)zoho-expense-pp-cli trips create- Create a tripzoho-expense-pp-cli trips delete- Delete a tripzoho-expense-pp-cli trips get- Get a tripzoho-expense-pp-cli trips list- List tripszoho-expense-pp-cli trips reject- Reject a tripzoho-expense-pp-cli trips update- Update a trip
users
Manage users in the organization
zoho-expense-pp-cli users activate- Mark a user as activezoho-expense-pp-cli users deactivate- Mark a user as inactivezoho-expense-pp-cli users delete- Delete a userzoho-expense-pp-cli users get- Get a userzoho-expense-pp-cli users invite- Invite a user into the orgzoho-expense-pp-cli users list- List userszoho-expense-pp-cli users me- Get the authenticated user's profilezoho-expense-pp-cli users update- Update a user
Output Formats
# Human-readable table (default in terminal, JSON when piped)
zoho-expense-pp-cli currencies list
# JSON for scripting and agents
zoho-expense-pp-cli currencies list --json
# Filter to specific fields
zoho-expense-pp-cli currencies list --json --select id,name,status
# Dry run — show the request without sending
zoho-expense-pp-cli currencies list --dry-run
# Agent mode — JSON + compact + no prompts in one flag
zoho-expense-pp-cli currencies list --agent
Agent Usage
This CLI is designed for AI agent consumption:
- Non-interactive - never prompts, every input is a flag
- Pipeable -
--jsonoutput to stdout, errors to stderr - Filterable -
--select id,namereturns only fields you need - Previewable -
--dry-runshows the request without sending - Explicit retries - add
--idempotentto create retries and--ignore-missingto delete retries when a no-op success is acceptable - Confirmable -
--yesfor explicit confirmation of destructive actions - Piped input - write commands can accept structured input when their help lists
--stdin - Offline-friendly - sync/search commands can use the local SQLite store when available
- Agent-safe by default - no colors or formatting unless
--human-friendlyis set
Exit codes: 0 success, 2 usage error, 3 not found, 4 auth error, 5 API error, 7 rate limited, 10 config error.
Freshness
This CLI owns bounded freshness for registered store-backed read command paths. In --data-source auto mode, covered commands check the local SQLite store before serving results; stale or missing resources trigger a bounded refresh, and refresh failures fall back to the existing local data with a warning. --data-source local never refreshes, and --data-source live reads the API without mutating the local store.
Set ZOHO_EXPENSE_NO_AUTO_REFRESH=1 to disable the pre-read freshness hook while preserving the selected data source.
Covered command paths:
zoho-expense-pp-cli currencieszoho-expense-pp-cli currencies createzoho-expense-pp-cli currencies deletezoho-expense-pp-cli currencies getzoho-expense-pp-cli currencies listzoho-expense-pp-cli currencies updatezoho-expense-pp-cli customerszoho-expense-pp-cli customers createzoho-expense-pp-cli customers deletezoho-expense-pp-cli customers getzoho-expense-pp-cli customers listzoho-expense-pp-cli customers updatezoho-expense-pp-cli expense_categorieszoho-expense-pp-cli expense_categories createzoho-expense-pp-cli expense_categories deletezoho-expense-pp-cli expense_categories disablezoho-expense-pp-cli expense_categories enablezoho-expense-pp-cli expense_categories getzoho-expense-pp-cli expense_categories listzoho-expense-pp-cli expense_categories updatezoho-expense-pp-cli expense_reportszoho-expense-pp-cli expense_reports approval_historyzoho-expense-pp-cli expense_reports approvezoho-expense-pp-cli expense_reports createzoho-expense-pp-cli expense_reports getzoho-expense-pp-cli expense_reports listzoho-expense-pp-cli expense_reports reimbursezoho-expense-pp-cli expense_reports rejectzoho-expense-pp-cli expense_reports updatezoho-expense-pp-cli expenseszoho-expense-pp-cli expenses createzoho-expense-pp-cli expenses getzoho-expense-pp-cli expenses listzoho-expense-pp-cli expenses mergezoho-expense-pp-cli expenses updatezoho-expense-pp-cli organizationszoho-expense-pp-cli organizations getzoho-expense-pp-cli organizations listzoho-expense-pp-cli projectszoho-expense-pp-cli projects activatezoho-expense-pp-cli projects createzoho-expense-pp-cli projects deactivatezoho-expense-pp-cli projects deletezoho-expense-pp-cli projects getzoho-expense-pp-cli projects listzoho-expense-pp-cli projects updatezoho-expense-pp-cli reporting_tagszoho-expense-pp-cli reporting_tags activatezoho-expense-pp-cli reporting_tags createzoho-expense-pp-cli reporting_tags deactivatezoho-expense-pp-cli reporting_tags deletezoho-expense-pp-cli reporting_tags getzoho-expense-pp-cli reporting_tags listzoho-expense-pp-cli reporting_tags list_optionszoho-expense-pp-cli reporting_tags updatezoho-expense-pp-cli taxeszoho-expense-pp-cli taxes createzoho-expense-pp-cli taxes deletezoho-expense-pp-cli taxes getzoho-expense-pp-cli taxes listzoho-expense-pp-cli taxes updatezoho-expense-pp-cli tripszoho-expense-pp-cli trips approvezoho-expense-pp-cli trips cancelzoho-expense-pp-cli trips closezoho-expense-pp-cli trips createzoho-expense-pp-cli trips deletezoho-expense-pp-cli trips getzoho-expense-pp-cli trips listzoho-expense-pp-cli trips rejectzoho-expense-pp-cli trips updatezoho-expense-pp-cli userszoho-expense-pp-cli users activatezoho-expense-pp-cli users deactivatezoho-expense-pp-cli users deletezoho-expense-pp-cli users getzoho-expense-pp-cli users invitezoho-expense-pp-cli users listzoho-expense-pp-cli users mezoho-expense-pp-cli users update
JSON outputs that use the generated provenance envelope include freshness metadata at meta.freshness. This metadata describes the freshness decision for the covered command path; it does not claim full historical backfill or API-specific enrichment.
Health Check
zoho-expense-pp-cli doctor
Verifies configuration, credentials, and connectivity to the API.
Configuration
Config file: ~/.config/zoho-expense-pp-cli/config.toml
Static request headers can be configured under headers; per-command header overrides take precedence.
Environment variables:
| Name | Kind | Required | Description |
|---|---|---|---|
ZOHO_EXPENSE_CLIENT_ID | auth_flow_input | Yes | OAuth client ID from api-console.zoho.in self-client |
ZOHO_EXPENSE_CLIENT_SECRET | auth_flow_input | Yes | Set during initial auth setup. |
ZOHO_EXPENSE_REFRESH_TOKEN | harvested | Yes | Populated automatically by auth login. |
ZOHO_EXPENSE_ORGANIZATION_ID | per_call | Yes | Zoho Expense organization ID (sent as X-com-zoho-expense-organizationid header on every request) |
Troubleshooting
Authentication errors (exit code 4)
- Run
zoho-expense-pp-cli doctorto check credentials - Verify the environment variable is set:
echo $ZOHO_EXPENSE_ORGANIZATION_IDNot found errors (exit code 3) - Check the resource ID is correct
- Run the
listcommand to see available items
API-specific
- HTTP 401 'authentication value invalid' — Run
zoho-expense-pp-cli auth refresh— the access token expired (1h lifetime). If that fails, the refresh token may have rotated out (max 20 per user); re-runauth loginwith a fresh authorization code. - HTTP 404 / endpoint not found from a .com token — Region mismatch. Your token was issued by accounts.zoho.com but you're targeting www.zohoapis.in. Override
base_urlin the config file or set ZOHO_EXPENSE_REGION accordingly. - Receipt upload returns 'autoscan_status: Failed' — File format or size issue — max 5MB; JPG/PNG/PDF only. Multi-page PDFs scan but slower. Try re-saving the PDF as image-only and retry.
- First expense for a new merchant has no category — Expected — Zoho only auto-categorizes after the merchant has been seen + categorized once. Use
merchant map '<name>' --category ...to seed the local mapping, thenexpense-untagged --auto-fix. - HTTP 429 rate-limited — Zoho caps at 100 req/min per org. The CLI's adaptive limiter handles this automatically; if you see this surface, you're running parallel batches — drop concurrency.
Sources & Inspiration
This CLI was built by studying these projects and resources:
- schmorrison/Zoho — Go (39 stars)
- ramp-cli — Python (28 stars)
- tdesposito/pyZohoAPI — Python (8 stars)
- airbyte source-zoho-expense — Python
Generated by CLI Printing Press
For agents: pass /documentation/zoho-expense/agents.md to install this CLI from a prompt.