solvebeam / twinfield-cli
Twinfield CLI brings Twinfield to your terminal. Free and open source.
Requires
- php: ^8.3
- ext-dom: *
- ext-json: *
- league/oauth2-client: ^2.9
- psr/http-client: ^1.0
- solvebeam/oauth2-twinfield: ^0.1.1
- solvebeam/twinfield: ^0.2
- solvebeam/twinfield-connect: ^0.1.1
- symfony/console: ^6.4 || ^7.0 || ^8.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.9
- phpstan/phpstan: ^2.3
- phpunit/phpunit: ^12
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Twinfield CLI brings Twinfield to your terminal. Free and open source.
The manual, with a page for each command, is on https://cli.twinfield.solvebeam.com/.
Log in to Twinfield once from the command line, then use that login in tests, examples and scripts:
composer require --dev solvebeam/twinfield-cli vendor/bin/twf login
use SolveBeam\Twinfield\Cli\Credentials; use SolveBeam\Twinfield\Twinfield; $twinfield = Twinfield::create(Credentials::default()); foreach ($twinfield->offices()->list() as $office) { echo $office->code, ' ', $office->name, PHP_EOL; }
It works like gcloud auth application-default login, aws sso login or gh auth login: the command line tool stores the login, and your code picks it up without knowing about OAuth.
You do not need a Twinfield client ID: by default the login goes through Twinfield Connect. The first login registers the Twinfield CLI with Twinfield Connect once per machine.
When to use it
- Tests and examples that call the live Twinfield API.
- Scripts you run yourself, such as an import or a report.
- CI, with environment variables instead of the login (see below).
In an application where users log in themselves, use solvebeam/twinfield-connect or solvebeam/oauth2-twinfield and store the refresh tokens yourself.
Commands
vendor/bin/twf login # log in with your browser vendor/bin/twf login --manual # paste the redirected URL yourself, e.g. over SSH vendor/bin/twf login --rest # also ask for all REST API scopes vendor/bin/twf status # show the logins vendor/bin/twf token # print a valid access token, e.g. for curl vendor/bin/twf logout # remove the login vendor/bin/twf office list # list the offices vendor/bin/twf process-xml # send an XML request vendor/bin/twf deleted-transaction list --office=1000 # list the deleted transactions vendor/bin/twf list # show all commands vendor/bin/twf login --help # show the options of a command
The command is twf, after the twf. prefix Twinfield uses itself (twf.organisationCode). vendor/bin/twinfield is the same command under its long name.
login opens the Twinfield login in your browser and catches the redirect on http://localhost:8765/callback.
Offices
office list lists the offices (companies, administrations) you have access to, like gh repo list:
vendor/bin/twf office list # all offices vendor/bin/twf office list '*Holding*' # search in code and name: * matches anything, ? one character vendor/bin/twf office list --limit=10 # or -L 10 vendor/bin/twf office list --json # JSON, e.g. for jq vendor/bin/twf office list --profile=acme # the offices of another login
In a terminal the offices are a table. In a pipe they are tab-separated lines without a header, with the code, name, country and company ID:
vendor/bin/twf office list | cut -f1
XML requests
process-xml sends a request to the XML web services of Twinfield (ProcessXml), like gh api. With it you read, create and change anything these web services support:
vendor/bin/twf process-xml request.xml --office=1000 echo '<read><type>office</type><code>1000</code></read>' | vendor/bin/twf process-xml --office=1000
xml is the short name of process-xml: vendor/bin/twf xml request.xml.
The request comes from a file or from standard input; --office is the code of the office (company) the request is for. The response is printed as Twinfield returned it, and indented in a terminal.
When Twinfield does not process (all of) the request, the response is printed too: it has the messages (msg) on the elements they are about. The error goes to standard error and the exit status is 1.
Deleted transactions
deleted-transaction list lists the transactions that were deleted in an office, the newest deletion first, for example to keep a copy of the transactions in sync:
vendor/bin/twf deleted-transaction list --office=1000 --from=2026-11-01 # deleted since 1 November vendor/bin/twf deleted-transaction list --office=1000 --day-book=BNK # of one day book vendor/bin/twf deleted-transaction list --office=1000 --limit=10 # the last 10 deletions vendor/bin/twf deleted-transaction list --office=1000 --json # JSON, e.g. for jq
--from and --to are about the date of deletion; both days are included. Twinfield has no paging here, so without --from you get all deleted transactions of the office.
In a terminal they are a table. In a pipe they are tab-separated lines without a header, with the day book, number, transaction date, moment of deletion, user and reason.
Profiles
Use a profile for each organisation:
vendor/bin/twf login --profile=acme
$twinfield = Twinfield::create(Credentials::default('acme'));
Or choose the profile with TWINFIELD_PROFILE=acme.
Your own Twinfield client
vendor/bin/twf login --client-id=… --client-secret=… --redirect-uri=http://localhost:8765/callback
The redirect URI must be registered with your client. Instead of the options you can set TWINFIELD_CLIENT_ID, TWINFIELD_CLIENT_SECRET and TWINFIELD_REDIRECT_URI.
Where the login is stored
In ~/.config/twinfield/credentials.json, or in $XDG_CONFIG_HOME/twinfield/ or $TWINFIELD_CONFIG_DIR/ when set. Only you can read the file (0600); on Windows the permissions of your user folder protect it.
The file holds the refresh token and the last access token. Credentials::default() uses the access token until it expires, then refreshes it and writes the new tokens back. Writes lock the file, so processes that run at the same time do not overwrite each other's refresh token.
CI and environment variables
When TWINFIELD_REFRESH_TOKEN is set, Credentials::default() uses it instead of the file. Credentials::default('acme') always uses the profile you name.
| Variable | |
|---|---|
TWINFIELD_REFRESH_TOKEN |
The refresh token. |
TWINFIELD_CONNECT_CLIENT_ID, TWINFIELD_CONNECT_CLIENT_SECRET |
The Twinfield Connect client, see credentials.json. |
TWINFIELD_CLIENT_ID, TWINFIELD_CLIENT_SECRET |
Or your own Twinfield client. |
TWINFIELD_CONNECT_URL |
Only for a Twinfield Connect you run yourself. |
TWINFIELD_REDIRECT_URI |
Only when your client does not use http://localhost:8765/callback. |
Tokens from environment variables are not written back.
License
The MIT License (MIT). See LICENSE.
Twinfield and Wolters Kluwer are trademarks of Wolters Kluwer. Their names are used only to refer to their products.