Description
dougs-compta



Unofficial TypeScript toolkit for Dougs, the online accounting firm that keeps the books of
my consulting company. Dougs has no public API, only the private one behind its web app. This repository wraps it in
a typed client, then builds two tools on top:
| Package | What it does |
| ———————————————— | ——————————————————————————————————————— |
| @plokkke/dougs-compta | Typed client: login with email verification code, idempotency-aware retries, validated responses, integer-cent inputs |
| @plokkke/dougs-cli | dougs export: the whole bookkeeping as lossless JSON + CSV, invoice documents included |
| @plokkke/n8n-nodes-dougs | n8n node: book expenses and mileage allowances, validate operations, upload receipts from workflows |
I built it to automate recurring bookkeeping chores (mileage allowances, receipts) from n8n, and to keep a full,
independent copy of my company’s accounting data.
Highlights
- Retries that cannot double-book. Transient failures (429, 5xx) are retried with backoff and
Retry-After, but a - Fail fast on API drift. Every response is validated with Zod. When Dougs changes a field, the client stops with the
- Typed yet lossless. Schemas type what the code relies on and keep every other field, so the export is a faithful
- Integer cents, calendar dates. Callers pass
amountCentsandYYYY-MM-DDdates. The conversion to the float euros - Tested against the contract, mutation-checked. Tests run against a scripted
fetch(no network), cover retries, - Safe exports. CSV cells that a spreadsheet would execute as formulas are neutralised, amounts go through integer
POST that creates an operation is only replayed after a 429, when the server provably did nothing. Full-state updates
opt in explicitly as idempotent.
offending path (0.amount: expected number) instead of writing a wrong figure into the books.
copy and not a projection of what this library happened to know.
the API expects happens in one audited place, and a date never travels as an instant a timezone could shift.
re-login on 401, pagination and payload shapes, and the SDK suite kills 91% of Stryker
mutants.
cents (0.30, never 0.30000000000000004), and document downloads are concurrency-limited and resumable.
Quick start
Export everything to ./export (raw JSON, CSV, manifest), with the invoice PDFs
DOUGSEMAIL=you@company.com DOUGSPASSWORD=... npx @plokkke/dougs-cli export --files
import { DougsClient } from '@plokkke/dougs-compta';const dougs = new DougsClient({ auth: { email: process.env.DOUGSEMAIL!, password: process.env.DOUGSPASSWORD! } });
const { company } = await dougs.getMe();
const expense = await dougs.registerExpense(company.id, {
date: '2026-03-31',
memo: 'Train Paris - Lyon',
amountCents: 8_450,
categoryId: 77,
partnerId: 5,
});
await dougs.validateOperation(company.id, expense.id);
Architecture
packages/
sdk/ HTTP layer (retries, auth hook) → session (login, cookie expiry) → client (endpoints + Zod schemas)
cli/ export: raw JSON writer, CSV columns, resumable concurrent downloads
n8n-nodes/ declarative operations → generated n8n properties, list-search lookups
The two tools only depend on the SDK’s public API; the SDK depends on nothing but Zod and the platform fetch.
Development
pnpm install
pnpm validate # build, lint, typecheck, test every package
pnpm mutation # Stryker mutation testing of the SDK
Releases are published by GitHub Actions when a v..* tag is pushed, with an npm provenance statement.
Node 20+ and pnpm are required. The git-ignored private/ folder is where local exports and personal analyses live. CI runs the validation on Node 20, 22 and 24.
Scope and responsibility
This project is not affiliated with Dougs. It only talks to the account of the person running it, with that person’s
own credentials, like the web app does; exporting is the right to data portability (GDPR, article 20) put into
practice. Requests are rate-limit friendly (bounded concurrency, Retry-After honoured). Credentials are read from the
environment or a git-ignored file, never logged, and exported data is git-ignored by default.