freeagent-cli
A small CLI for submitting FreeAgent timeslips without clicking through the web UI.
freeagent-cli log acme 1h30m "fixed the thing"
Install
uv tool install freeagent-cli
One-time setup
You'll need to register your own OAuth app with FreeAgent. It takes about two minutes and keeps your data and rate limits separate from everyone else's.
-
Go to https://dev.freeagent.com/apps and create a new app.
-
Set the redirect URI to:
http://localhost:7878/callback -
Note the OAuth identifier and OAuth secret.
-
Save them locally:
freeagent-cli auth init --client-id <id> --client-secret <secret>Add
--sandboxif you want to test against the FreeAgent sandbox first. -
Authorise the app in your browser:
freeagent-cli auth loginA browser tab opens, you approve, and the CLI captures the refresh token. The refresh token lasts ~20 years; access tokens auto-refresh on every command.
Credentials are stored at ~/Library/Application Support/freeagent-cli/config.json (macOS) or the equivalent platform config directory, with file mode 0600.
Usage
freeagent-cli --help # canonical flow
freeagent-cli recent # what you've already logged (run this first to avoid duplicates)
freeagent-cli log <project> <duration> [comment...] # submit a timeslip
freeagent-cli projects # first-time / discovery: projects + tasks in one call
freeagent-cli accounts # bank accounts + balances
freeagent-cli unexplained # bank transactions still needing an explanation
Examples:
freeagent-cli log Acme 1h30m "fixed the thing"
freeagent-cli log Acme 90m fixed the thing # comment without quotes
freeagent-cli log "Big Co" 1.5 --task Coding --date 2026-05-01
freeagent-cli log Acme 1.5 --dry-run # preview, don't submit
- Duration accepts
1.5,90m,1h30m, or1:30. - Project / task match by case-insensitive name substring, numeric id, or full URL.
--taskis optional when the project has a single task; otherwise the error lists the choices.--datedefaults to today (ISOYYYY-MM-DDto override).--dry-runresolves the project/task/date and prints the would-be submission without sending it.projects --flatemits one project/task pair per line (tab-separated) for grep/awk.
Banking
freeagent-cli accounts # id, name, currency, balance
freeagent-cli unexplained # what still needs explaining
freeagent-cli unexplained --account Current --days 365
freeagent-cli unexplained -n 0 | grep -i stripe # the whole backlog, filtered
unexplained prints one tab-separated transaction per line — date, unexplained amount, description, count of similar transactions, marker, URL — most recent first. The marker reads partial when only part of a transaction has been explained.
--accountis optional if you have a single active bank account; otherwise the error lists the choices. Matches by name substring, id, or URL, like--project.--daysdefaults to 90. Pass0for no date limit.-ndefaults to 25. Pass0for all.- A summary (count and total) goes to stderr, so piping stdout into
grep/awkstays clean.
Hidden accounts are left out of accounts unless you pass --all, and are never picked as the implicit default for unexplained. Naming one explicitly with --account still works.
Explaining transactions
freeagent-cli categories --search travel # find a category
freeagent-cli explain 12345 285 --dry-run # preview
freeagent-cli explain 12345 285 --description "train fare" # submit
freeagent-cli explain 12345 285 --amount 20 # explain part of it
categories lists nominal code, group and description, one per line. Filter with --search or --group (admin_expenses, cost_of_sales, income, general).
- CATEGORY matches by nominal code (
285), full URL, or case-insensitive description substring. An ambiguous match lists the candidates with their codes. --like <transaction>reuses the category from a transaction you've already explained, instead of naming one. See below.--amountexplains part of a transaction; it defaults to the whole unexplained amount and can't exceed it. Currency symbols and commas are ignored.- The sign always comes from the transaction, so
--amount 20on a payment of-42.50explains-20.00. You can't accidentally book a spend as income. --datedefaults to the transaction's own date.--dry-runpreviews; otherwise you're asked to confirm.-yskips the prompt.
VAT: explain doesn't set sales-tax fields, so FreeAgent applies the category's automatic rate. If a transaction needs a non-standard rate, EC status, or a manual VAT amount, do that one in the web UI.
Recurring payees
Most of a backlog is the same handful of payees every month. --like copies the category from a transaction you've already explained, so you don't have to remember which one it was:
freeagent-cli explain 12345 --like 9999
The similar column in unexplained (from matching_transactions_count) tells you when a transaction has precedent worth copying.
--like reads the category and nothing else — the amount, date and description still come from the transaction being explained, or from your flags. It refuses rather than guesses when there's no single answer:
- The source has no category — invoice payments, bill payments and transfers aren't categorised.
- The source is split across several categories, in which case the error lists them so you can pick one.
A typical backlog session:
freeagent-cli unexplained
freeagent-cli explain 12345 285 --description "client dinner"
freeagent-cli explain 12346 --like 12345
License
MIT
Release files for freeagent-cli 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| freeagent_cli-0.6.0.tar.gz | 32.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| freeagent_cli-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.0 kB
Release files / freeagent_cli-0.6.0.tar.gz
| Download URL | freeagent_cli-0.6.0.tar.gz |
|---|---|
| Size | 32.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0a84b78085c1c2e43e7648f3ebf1b97ed32b20d8e04155147ac891346469d81a
|
|
BLAKE2b-256 checksum How to use checksums |
6f6d755b3e20eab79d24902157a5640aad5cedd8f6f904882b0c45953d1982d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / freeagent_cli-0.6.0-py3-none-any.whl
| Download URL | freeagent_cli-0.6.0-py3-none-any.whl |
|---|---|
| Size | 18.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5b2132c5dd0140e4430b717d4a04f6cced9e4ade7c506e4aacb904106809c978
|
|
BLAKE2b-256 checksum How to use checksums |
09689366334886e44a189f5fef3c8a9936b2e596b105037fe6d1cb82ed315d7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency log