damacus/freeagent-cli

FreeAgent CLI

★ 0Forks 0GoGitHub ↗Compare

README

freeagent

A small CLI for the FreeAgent API, built in Go.

Features

  • OAuth login (local callback or manual paste)
  • Keychain-backed token storage with file fallback
  • Create and send invoices
  • Inspect VAT, corporation tax and Self Assessment returns, deadlines and payment status
  • Export invoice, estimate and credit-note PDFs; duplicate and convert documents
  • Manage FreeAgent filing/payment markers, default text, price-list items and timers
  • Break-glass raw command for any FreeAgent endpoint
  • JSON output mode for scripting / agents

Install

go build -o freeagent .

Configure

Create a FreeAgent API application and note the client ID + secret.

Save app credentials:

./freeagent auth configure \
  --client-id YOUR_ID \
  --client-secret YOUR_SECRET \
  --redirect http://127.0.0.1:8797/callback

You can also use env vars:

export FREEAGENT_CLIENT_ID=...
export FREEAGENT_CLIENT_SECRET=...
export FREEAGENT_REDIRECT_URI=http://127.0.0.1:8797/callback

Login

Local callback (default):

./freeagent auth login

Manual flow:

./freeagent auth login --manual

Usage

The API coverage matrix compares documented operations, flags and remaining gaps. It distinguishes dedicated commands from raw API access.

View tax returns and their breakdowns:

./freeagent vat-returns list --page 1 --per-page 100
./freeagent vat-returns get 2026-06-30
./freeagent corporation-tax-returns list
./freeagent self-assessment-returns list --user 119
./freeagent final-accounts-reports get 2025-12-31
./freeagent --json vat-returns get 2026-06-30

Filing markers record status in FreeAgent. They do not submit returns to HMRC or Companies House. Payment markers do not transfer money. Preview changes with --dry-run before running the same command without that flag:

./freeagent vat-returns mark-filed --dry-run 2026-06-30
./freeagent vat-returns mark-paid --dry-run --payment-date 2026-08-07 2026-06-30
./freeagent corporation-tax-returns mark-paid --dry-run 2025-12-31
./freeagent self-assessment-returns mark-unpaid --dry-run --user 119 --payment-date 2027-01-31 2026-04-05

Document and accounting workflows:

./freeagent invoices pdf --output invoice-123.pdf 123
./freeagent estimates duplicate --dry-run 42
./freeagent estimates convert-to-invoice --dry-run 42
./freeagent invoices update --dry-run --body invoice-update.json 123
./freeagent invoices default-text set --dry-run --text 'Payment due within 30 days'
./freeagent timeslips start-timer --dry-run 456
./freeagent bank-feeds list
./freeagent hire-purchases list
./freeagent expenses mileage-settings
./freeagent account-locks list

New ID-based commands accept a numeric ID or a URL for that resource on the configured API origin. Tax commands use a period-end date. PDF output creates a new file and refuses overwrite; use --json instead of --output to receive the base64 PDF API envelope. Invoice and journal updates accept either a JSON object of fields or an object wrapped in invoice / journal_set. Existing CLI flags and JSON output remain available. Use each command's --help for required flags.

Account locks and practice administration:

./freeagent account-locks list
./freeagent account-locks set --dry-run --locked-to-date 2026-03-31
./freeagent account-locks delete --dry-run
./freeagent account-managers list --page 1 --per-page 100
./freeagent account-managers get 123
./freeagent account-managers get me
./freeagent clients list --view active --sort=-updated_at
./freeagent clients list --minimal-data --page 1 --per-page 500
./freeagent practice get

practise get is an alias for practice get. Practice endpoints require a practice-enabled application and an authorised account manager. Client lists fetch one page per request and support --from, --to and --updated-since. The usual maximum is 100 clients per page, rising to 500 with --minimal-data. Only the user account lock can be set or removed; deletion requires --yes unless using --dry-run. FreeAgent validates the permitted lock date range.

Receipt uploads already attach files through their parent records:

  • bills create/update --receipt FILE
  • expenses create/update --receipt FILE
  • bank explain create/update --receipt FILE
  • bank review attach-receipt --explanation ID --file FILE

These commands embed the file contents, filename and detected content type in the parent request. Standalone attachments get ID returns metadata, expiring content_src download URLs and expires_at; it does not download the file. attachments delete --yes ID removes the attachment. Use --dry-run to preview deletion. The documented standalone attachment API has no list or upload route.

Nested operations use the documented wrapped JSON payload with --body:

./freeagent estimate-items create --dry-run --body estimate-item.json
./freeagent estimates send --dry-run --body estimate-email.json 42
./freeagent credit-notes send --dry-run --body credit-email.json 19
./freeagent bank import-statement --dry-run --bank-account 7 --body statement.json
./freeagent cis-settings update --dry-run --body cis-settings.json
./freeagent invoices direct-debit --dry-run 123

Example estimate-item.json:

{
  "estimate": "https://api.freeagent.com/v2/estimates/42",
  "estimate_item": {"item_type": "Days", "quantity": "1", "price": "500.00", "description": "Development"}
}

Example estimate-email.json, using an existing FreeAgent email template:

{"estimate": {"email": {"use_template": true}}}

Credit-note email payloads use credit_note.email with to, from, subject and body. The sender must be a registered user. CIS updates use a cis_settings object; setting a registration section to null deregisters it. See the linked coverage matrix for official payload details.

Example statement.json:

{"statement": [{"dated_on": "2026-09-01", "amount": "-100.00", "description": "Supplier", "fitid": "txn-123"}]}

Statement upload success does not prove that import completed. Check with bank list --bank-account 7 or in FreeAgent afterwards. Include all of a day's transactions in an upload to avoid incorrect deduplication. This command supports JSON transactions or multipart OFX/QBO/QIF/CSV files up to 16 MiB.

Unlike tax status markers, invoices direct-debit collects payment through an eligible GoCardless mandate. It requires --yes to run, or --dry-run to preview.

Create a draft invoice:

./freeagent invoices create \
  --contact CONTACT_ID \
  --reference INV-001 \
  --lines ./invoice-lines.json

You can also pass a contact name or email and the CLI will resolve it:

./freeagent invoices create \
  --contact "Acme Ltd" \
  --reference INV-002 \
  --lines ./invoice-lines.json

Send an invoice email:

./freeagent invoices send --id INVOICE_ID --email-to [email protected]

Mark as sent (no email):

./freeagent invoices send --id INVOICE_ID

Break-glass request:

./freeagent raw --method GET --path /v2/invoices

Contacts:

./freeagent contacts list
./freeagent contacts search --query "Acme"
./freeagent contacts get --id CONTACT_ID
./freeagent contacts create --organisation "Acme Ltd" --email [email protected]

Bank transactions (bulk approve):

./freeagent bank approve \
  --bank-account BANK_ACCOUNT_ID \
  --from 2025-01-01 \
  --to 2025-01-31

./freeagent bank approve --ids ./transaction-ids.txt
./freeagent bank approve --ids ./explanation-ids.txt --ids-type explanation

Files

  • Config: ~/.config/freeagent/config.json
  • Tokens (fallback): ~/.config/freeagent/tokens/PROFILE.json

Notes

  • Default API base URL is production; use --sandbox for the sandbox API.
  • Use --json to print raw JSON for automation or piping into other tools.

License

MIT. See LICENSE.

Bank statements can also be uploaded with bank import-statement --bank-account ID --file statement.ofx (OFX/QBO/QIF/supported CSV, maximum 16 MiB). Use --dry-run to preview and recheck bank list to verify import.

Multiple bank explanation attachments

bank explain attachments list --explanation 7 lists all attached files. Use upload --explanation 7 --file receipt.pdf to add a file, or update --explanation 7 --attachment 3 --file replacement.pdf to replace one. delete --explanation 7 --attachment 3 --yes removes that attachment. Write commands support --dry-run; upload and update accept --description. These commands select API version 2026-09-01 automatically.

Use bank --api-version 2026-09-01 before existing explain and review commands to select the multiple-attachment API. Existing --receipt and review attach-receipt then add a file through the dedicated attachment endpoint. Without this option the server default and legacy receipt payload are retained while supported. FreeAgent changes its default on 1 December 2026; select the explicit version when migrating. Reviews recognise both response shapes. An explanation and its receipt require separate requests: an upload failure reports the saved explanation so you can retry the attachment without duplicating it.

bank list --bank-account 7 --last-uploaded selects the latest statement upload. Bank listing follows next-page links and preserves filters and unknown JSON fields.

Versioned receipt writes return the saved explanation and attachment responses without a follow-up read. If approval fails after upload, the result reports receipt_uploaded: true, approved: false and a warning to retry approval only. Aggregated bank lists retain per-page top-level fields under page_metadata; transaction fields remain unchanged. Single-page responses retain their original metadata.

Contributors

damacusanjorrenovate[bot]github-actions[bot]

Issues