11b82cbb91
* feat(api): installable accounted-api agent skill + openapi-to-skill generator Three layers, per the July/August 2026 agent-skills ecosystem (skills.sh / npx skills add, as used by Stripe/Cloudflare/Supabase for their APIs): - skills/openapi-to-skill/: generic, installable skill that turns any OpenAPI spec into a consumer-side integration skill, with a portable stdlib-only inventory/condenser tool and an output template + quality checklist encoding the distill-not-restate methodology. - skills/accounted-api/: the installable skill for our own API, rendered deterministically by scripts/api-skill/generate.ts from the v1 endpoint registry + hand-authored overlays (auth, conventions, domain gotchas). CI gate: npm run apiskill:check (core-build.yml). - lib/api/v1/registry.ts: generateOpenApiSpec now emits requestBody (incl. multipart binary parts) and path parameters, and the Zod converter learned .default()/z.record()/.pipe()/.transform(), so the public spec carries request contracts instead of prose-only. Docs: /docs/api landing + /llms.txt now point agents at the skill install; corrected the stale test-key description in the landing (test keys read real data and force dry-run writes; they are not sandbox-company bound). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): escape backslashes in markdown table cells (CodeQL js/incomplete-sanitization) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2.3 KiB
2.3 KiB
Gotchas (Swedish accounting domain)
Rules a generic REST integration will violate unless told:
- Account numbers are strings, not numbers. BAS accounts (
"1930","3001") are identifiers; send them as JSON strings. Arithmetic on them, zero-stripping, or number coercion corrupts postings. - Posted journal entries are immutable by law (Bokföringslagen). There is
no PATCH or DELETE on a committed entry, ever. Undo with
POST .../journal-entries/{id}/reverse(storno), fix withPOST .../journal-entries/{id}/correct. Design flows around reverse-and-repost, not edit-in-place. - Voucher numbers are gapless and server-assigned. Never assume or
pre-allocate one; read it from
meta.audit.voucher_numberafter commit. A legally required gap explanation goes throughPOST .../voucher-gap-explanations. - Every entry balances.
sum(debit) === sum(credit)to the öre, amounts are decimal SEK numbers (max 2 decimals). Do rounding with round-half-away-from-zero on öre; never float-accumulate line totals client-side and "fix" the difference on a random line. - Period locks are a feature, not an error to retry. Writes into a
locked/closed period return
PERIOD_LOCKED(withvalid_alternativespointing at open periods). Retrying the same request cannot succeed; either target an open period or surface the lock to the user. - Drafts vs posted. Invoices are created as drafts with
invoice_number: null; the F-series number is assigned atomically on send. Journal entries follow draft -> commit. Nothing financial exists in the ledger until the commit/send action. - Two invoice worlds.
invoices= accounts receivable (you bill customers);supplier-invoices= accounts payable (you receive bills). They are different resources with different lifecycles. - Swedish user-facing text.
error.messageis Swedish by design; show it to Swedish end users, and usemessage_enfor your own logs/logic. Domain terms in responses (moms, verifikat, kostnadsställe) are not translatable labels but legal concepts. - Compliance pre-flight. Before building your own validation for Swedish
rules, call
GET .../compliance/check: it runs the server's own rule set (VAT plausibility, sequence integrity, period status) and returns findings.