@taskmagic/apps-fakturoid
v0.0.1
Published
Czech invoicing with [Fakturoid](https://www.fakturoid.cz) — API docs: https://www.fakturoid.cz/api/v3
Readme
Fakturoid
Czech invoicing with Fakturoid — API docs: https://www.fakturoid.cz/api/v3
Connection: a Client ID and Client Secret from a Fakturoid OAuth application
(Settings → User account → API), plus your account slug — the part of your Fakturoid URL
after the domain, e.g. app.fakturoid.cz/my-company/....
Use the Client Credentials flow when creating the application. The Authorization Code flow also exists but is for multi-tenant apps; a flow authorising one account wants client credentials. The piece exchanges these for a short-lived bearer token at request time and caches it until shortly before expiry, so a multi-step flow doesn't mint a token per step.
Base URL: https://app.fakturoid.cz/api/v3
Actions
List Invoices, Get Invoice, Create Invoice, Mark Invoice Paid, List Subjects, Create Subject, List Expenses, Get Account.
Mark Invoice Paid posts to the invoice's payments sub-resource rather than setting a status
field — that's what actually moves the invoice to paid, and it records a partial payment
correctly if you set an amount.
Create Invoice takes Lines as an array of objects, each with at least name, quantity
and unit_price:
{"name": "Consulting", "quantity": "10", "unit_price": "1000", "vat_rate": 21, "unit_name": "h"}Fakturoid calls contacts subjects, which is why the invoice actions ask for a Subject ID.
Triggers
New Invoice, New Subject — both polling.
Fakturoid has a webhooks resource, but registering one needs a target URL confirmed in its UI and
the public v3 reference documents no create/delete pair to drive from onEnable/onDisable. Both
triggers key off created_at rather than updated_at, so an invoice doesn't re-fire every time
its status changes.
They page through results rather than reading only the first 40. Time-based dedupe advances its cursor to the newest record it sees, so if a burst spilled onto page 2 those records would be older than the new cursor and lost permanently. Paging stops as soon as a page comes back short or reaches the previous cursor, so the steady state is still one request.
Two API requirements that bite if missed
- A
User-Agentnaming the application and a contact address is mandatory. Omitting it returns 400, not 401 — which reads like a malformed body rather than a header problem. - Every resource path is scoped to the account slug — including the account detail, which
is
/accounts/{slug}/account.jsonand not/account.json. Probed:GET /api/v3/account.json→ 404,GET /api/v3/accounts/{slug}/account.json→ 401. There is no unscoped path.
