188816652d
Brings the developer-facing API and MCP docs back in line with origin/main (audit 2026-08-26). Docs only; no runtime behaviour changes. - Tool counts: the server registers 153 tools; docs said 90+/100+/120. All now say "150+" (connect-claude, gnubok-mcp README, plugin README, mcp-server rules, CLAUDE.md, registry entry with refreshed updatedAt). Not derived from the tools array: lib/ must not import @/extensions/. - REST changelog: backfilled the additive 2026-08 changes (#1909 report date ranges + PDFs, #1864 POST /companies, #1773 vat-declarations, #1405 PATCH settings, #1724/#1788 customer personal_number, #1809 cash_account_id filter). API version date unchanged. - Version headers: Gnubok-Deprecation is planned, not emitted; the Gnubok-Version request header is not read today (version.ts comment, versioning page, conventions overlay, regenerated skills/accounted-api). - connect-claude Path A documents lazy auth (connector works before an account exists; sign-in on the first company-scoped call). - MCP server README: real Anthropic SDK call sites, real resource URIs, pending-operations widget, public-tools/tasks/origin-guard/pii-guard. Rules file gains Lazy auth + feedback/tasks paragraphs. - api-routes endpoint map regenerated from the filesystem (560 routes, 55 families incl. v1, agent, reconciliation account-keyed, dimensions, peppol, rot-rut, webshop-orders, mileage, billing, skatteverket, receipt-hunt). - gnubok-mcp/accounted-mcp: /settings?tab=api is the pre-redesign URL; now /settings/api (README + help hints, no version bump). Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
80 lines
4.0 KiB
Markdown
80 lines
4.0 KiB
Markdown
## Conventions
|
|
|
|
These rules hold across the whole surface; endpoint entries below do not
|
|
repeat them.
|
|
|
|
**Response envelope.** Success: `{ "data": ..., "meta": { "request_id",
|
|
"api_version", "next_cursor"?, "audit"?, "partial_expansions"? } }`. Errors
|
|
replace `data` with `error` (no `meta`; `request_id` moves inside `error`).
|
|
|
|
**Errors.** Stable machine codes with agent-oriented remediation:
|
|
|
|
```json
|
|
{ "error": { "code": "PERIOD_LOCKED", "message": "Svenska", "message_en": "English",
|
|
"details": {}, "recovery_hint": "next step", "docs_url": "...",
|
|
"valid_alternatives": {}, "request_id": "req_..." } }
|
|
```
|
|
|
|
React to `code`, read `message_en` and `recovery_hint`, follow
|
|
`valid_alternatives` when present (e.g. `next_open_period`). Standard codes on
|
|
every endpoint: `400` validation, `401` bad key, `403` missing scope, `404`,
|
|
`429` rate limited (honor `Retry-After`), `500`. Full catalogue:
|
|
https://app.gnubok.se/docs/api/errors. Only endpoint-specific codes are
|
|
mentioned per endpoint below.
|
|
|
|
**Cursor pagination.** List endpoints take `?cursor=` and return
|
|
`meta.next_cursor`; loop until it is absent/null. A stale or tampered cursor
|
|
is NOT an error: the first page is returned again, so terminate on
|
|
`next_cursor`, never on "page looks familiar".
|
|
|
|
**Dry-run on every write that supports it** (`dry-run` badge in the index).
|
|
Send `?dry_run=true` (or `X-Dry-Run: true`): the response is always `200` with
|
|
`data.dry_run: true` plus a preview (would-be record, journal lines, voucher
|
|
number) and the `X-Dry-Run: true` response header; nothing is committed.
|
|
Commit by re-issuing without the flag and with the SAME `Idempotency-Key`
|
|
(the dry run is not cached against the key). Preview first on any financial
|
|
write; it is free.
|
|
|
|
**Idempotency-Key.** Send a fresh UUID header on every POST/PATCH/DELETE;
|
|
several endpoints reject writes without one (`400`). Replaying the same
|
|
key+body returns the original response with the `Idempotent-Replayed: true`
|
|
header; the same key with a different body returns `409 IDEMPOTENCY_KEY_REUSE`
|
|
(24h window). Safe retry loop: keep the key, keep the body.
|
|
|
|
**Test keys are simulation-only.** With a `gnubok_sk_test_*` key, reads
|
|
return real company data (responses carry `X-Gnubok-Mode: test`) and every
|
|
write is forced into dry-run; writes that cannot be simulated return
|
|
`403 TEST_KEY_WRITE_BLOCKED`. Nothing a test key does ever persists: it is
|
|
`?dry_run=true` baked into the credential. Full end-to-end write tests
|
|
therefore need a live key against a company you own.
|
|
|
|
**Atomic writes.** A mutation either commits fully or errors with no side
|
|
effects. There is no partial state to clean up after an error response
|
|
(`bulk-create` endpoints that do partial success say so explicitly).
|
|
|
|
**Audit inline.** Successful financial writes include `meta.audit` (voucher
|
|
number, audit-trail URL, immutability timestamp). No follow-up read needed to
|
|
confirm what was booked.
|
|
|
|
**Expansion.** Some list/detail endpoints take `?expand=a,b` (documented per
|
|
endpoint). If an expansion fails the response still succeeds and names the
|
|
failed parts in `meta.partial_expansions`; check it before trusting expanded
|
|
fields.
|
|
|
|
**Async operations.** Long-running actions (fiscal-period lock/close/year-end,
|
|
imports) return `202` with an operation id; poll `GET /api/v1/operations/{id}`
|
|
until `status` is `succeeded`/`failed`. The response shape is identical
|
|
whether the work ran inline or queued.
|
|
|
|
**Versioning.** Dated versions (current: see `meta.api_version`). Responses
|
|
carry the `Gnubok-Version` header; request pinning via a `Gnubok-Version`
|
|
request header is reserved for a future breaking change and is not read
|
|
today. Additive changes ship without a version bump; see
|
|
https://app.gnubok.se/docs/api/versioning.
|
|
|
|
**Index badges.** Every operation line below carries machine-readable
|
|
annotations from the spec: `scope:` (required key scope), `risk:` (low/medium/
|
|
high; confirm with a human before unprompted high-risk calls), `idempotent`
|
|
(safe to retry), `dry-run` (previewable), `reversible` (a single follow-up
|
|
call can undo it, e.g. invoice credit).
|