An F# CLI tool and client library for the NoCFO Finnish accounting API.
Bootstrapped from nocfo-api-onboard, an exploration repo that documents five approaches tried before arriving at this one. The full narrative — lessons learned, earlier iterations, exploratory FSI scripts — lives there.
hawaii-client/– F# library using Hawaii-generated types, lazyAsyncSeqstreams, and a thin domain layer. Start here.api/openapi.json– The upstream NoCFO OpenAPI document used for generation.tools/– CSV-first CLI ("nocfo") built on top of the Hawaii F# client library; seetools/README.md.tests/– xUnit unit tests for the pure library modules (no network access needed).tests-online/– Shell-based online regression tests for command line commands againstapi-tst.requests/– Raw HTTP checks (VS Code REST client format) for manual smoke testing.vendor/Hawaii/– Forked generator with small fixes for nullable handling, enum tolerance, and operation name cleanup.
The CLI in tools/ is the easiest way to interact with the API. It streams
entities, writes them as CSV, and can reconcile edited rows back to the server.
Prebuilt self-contained binaries are published on GitHub Releases for osx-arm64,
linux-x64, and win-x64. Pushing a tag named vX.Y.Z triggers CI to build
and attach nocfo-<tag>-<rid> archives to that release.
-
Prerequisites
- .NET 10 SDK (
dotnet --version≥ 10.0) - macOS or Linux shell (the code itself is cross-platform)
- Auth token and base URL supplied via either a named profile or environment variables:
- Named profile: create
~/.config/nocfo/config.toml(seetools/README.md) and pass--profile <name> - Environment variables:
NOCFO_TARGET_TOKEN(or fallbackNOCFO_TOKEN); optionallyNOCFO_TARGET_BASE_URL(defaults tohttps://api-tst.nocfo.io)
- Named profile: create
NOCFO_SOURCE_TOKEN(or a profile withsource_token) only when running dual-environment commands likemap accounts- If a local
.envexists, you maysource .envto populate tokens and aliases for your shell session. The GitHub version of this repo does not include.env.
# Option A: env vars export NOCFO_TOKEN="paste-your-token" # Option B: named profile (see tools/README.md for config file format) # dotnet run --project tools -- --profile test list businesses
- .NET 10 SDK (
-
Build once and run tests (from repo root):
dotnet build make testThe online regression suite is separate and requires local api-tst credentials that need to be first configured, see
tests-online/README.md. -
List businesses:
dotnet run --project tools -- \ list businesses --fields "id,name,slug" > businesses.csv
-
List accounts for a business:
dotnet run --project tools -- list accounts \ -b <business-id> \ --fields "id,number,name,type" > accounts.csv
-
List documents for a business:
dotnet run --project tools -- list documents \ -b <business-id> \ --fields "id,number,date,balance" > documents.csv
-
Update accounts by editing the CSV (keep
id) and piping it back in:dotnet run --project tools -- update accounts \ -b <business-id> \ --fields "id,number,name" < accounts.csv
-
Update contacts by editing exported contacts (keep
id) and piping them back in:dotnet run --project tools -- update contacts \ -b <business-id> \ --fields "id,name,invoicing_email,notes" < contacts.csv
-
Delete accounts by piping a CSV with the account
ids you want to drop:dotnet run --project tools -- delete accounts \ -b <business-id> < ids-to-delete.csv
-
Map account IDs between environments (source -> target by account
number):NOCFO_SOURCE_TOKEN="paste-source-token" NOCFO_TARGET_TOKEN="paste-target-token" \ dotnet run --project tools -- map accounts \ -b <business-id> > csv/account-id-map.csv
-
Create minimal documents in target:
dotnet run --project tools -- create documents \
-b <target-business-id> \
--account-id-map csv/account-id-map.csv \
< csv/documents-create.csv--profile <name>/-p <name>selects a named profile from~/.config/nocfo/config.toml. Environment variables always override profile values when both are set.--dry-runprints what mutations would be sent without executing them.--fieldscontrols both which columns are emitted and which columns are read back.idis always required when executing updates or deletes.- Output defaults to stdout and input defaults to stdin;
--out/--inoverride those streams without shell redirection. - Currently implemented verbs include all
listcommands;update businesses,update accounts,update contacts;delete accounts,delete contacts,delete documents;create businesses,create accounts,create contacts, and minimalcreate documents; plusmap accounts. - Errors and HTTP traces go to stderr so you can keep piping stdout to files.
See tools/README.md for a deeper dive into configuration, CSV expectations,
and the internal architecture.
The repo includes a separate shell-based online test suite under tests-online/.
It exercises the existing CLI end to end against https://api-tst.nocfo.io and is
not part of the default dotnet test flow.
Setup:
- Copy
tests-online/config/config.toml.exampletotests-online/config/config.toml. - Set a real api-tst token in the
online-testprofile. - Set
TEST_BUSINESS_SLUGintests-online/config/fixture.env. - Ensure
bashandpython3are available in your shell environment.
Run it explicitly with:
make test-onlineCI publishes release archives automatically when a tag matching v* is pushed.
Current release targets:
osx-arm64linux-x64win-x64
Assets are named nocfo-<tag>-<rid>.tar.gz on Unix targets and
nocfo-<tag>-<rid>.zip on Windows. Each archive contains the full self-contained
publish directory for that platform.
The generated client under hawaii-client/generated/ is checked in, so the repo builds without a regeneration step.
Regenerate only when the NoCFO OpenAPI spec changes.
-
Refresh
api/openapi.json.curl -L --fail --silent --show-error \ -H "Accept: application/vnd.oai.openapi+json;version=3.0" \ "https://api-tst.nocfo.io/openapi/" \ -o api/openapi.json
-
Build the local Hawaii fork:
dotnet build vendor/Hawaii/src/Hawaii.fsproj -c Release
-
From the repo root, run the built Hawaii CLI against the curated config:
dotnet ./vendor/Hawaii/src/bin/Release/net10.0/Hawaii.dll \ --config ./hawaii-client/nocfo-api-hawaii.json \ --no-logo
-
Rebuild everything:
dotnet build
Notes:
hawaii-client/nocfo-api-hawaii.jsoncurrently assumes you run Hawaii from the repo root:schemais resolved from the current working directory, whileoutputis resolved relative to the config file.
For more step-by-step build instructions, see hawaii-client/README.md.
- Lazy streaming over paginated endpoints –
AsyncSeqwrappers provide pull-based iteration over businesses and accounts while preserving the original pagination semantics. - Hydratable domain model –
Hydratablediscriminated unions keep initial payloads lightweight and defer full record loading until needed by folds or reports. - Token-based auth done correctly – All HTTP helpers attach the
Authorization: Token <value>header per request, preventing subtle errors with .NET's default header validation. - Structured HTTP errors and retry –
Http.fsreturns a typedHttpErrorDU and retries 429/5xx responses automatically with exponential backoff.
The design settled here is the fifth iteration. Earlier approaches (TypeScript, PureScript,
F#+NSwag) are documented in the
nocfo-api-onboard exploration repo,
along with LESSONS-LEARNED.md and exploratory FSI scripts that are not carried over here.
Active development. See ROADMAP.md for the current plan.
If you use any part of this repo, please do so at your own discretion. Contributions are welcome.