Menu
Cookbooks
API reference
- Overview
- Companies
- Capabilities
- Measures
- Packages
- Unexplained
- Liquidity forecast
- Portfolio
- Customers
- Invoices
- Collections
- Articles
- Set-offs
- Suppliers
- Supplier invoices
- Supplier payment files
- Bank payments
- Expense claims
- Transactions
- Reconciliation
- Bank accounts
- POS sales
- Webshop orders
- Journal entries
- Voucher gap explanations
- Fiscal periods
- Accounts
- Accrual schedules
- Agreements and commitments
- Tax loss carry-forwards
- Fixed assets
- Documents
- Signing requests
- Company registers
- Capital events
- Owner tax
- Inbox items
- Dimensions
- Employees
- Salary runs
- Reports
- Budgets
- Imports
- Compliance check
- Skatteverket
- Peppol
- Webhooks
- Operations
- Pending operations
- Health
- API key
- Service accounts
Unexplained
The queue of differences that should be zero: kundreskontra against booked receivables, leverantörsreskontra against booked payables, bank against books, skattekonto against Skatteverket, declared moms not moved to the settlement account, and bank rows unbooked past the booking deadline. Each result is ok, unexplained or cannot_check with a reason, reads both figures from the reports a person sees, and explains itself down to the invoices, verifikat and bank rows behind it.
Endpoints
GET/api/v1/companies/:companyId/unexplained: The unexplained queue: every expected-zero check run for the company, differences first.GET/api/v1/companies/:companyId/unexplained/:checkId: One expected-zero check: its definition and basis, the figures it compares, and every result.GET/api/v1/companies/:companyId/unexplained/:checkId/explain: What a difference in the queue consists of: its parts and the items behind them, with typed handles.
GET /api/v1/companies/:companyId/unexplained
unexplained.list · scope reports:read
The unexplained queue: every expected-zero check run for the company, differences first.
Runs every check of a difference that should be zero and lists one row per result: kundreskontra against booked receivables, leverantörsreskontra against booked payables, each bank account's booked balance against the balance the bank reported, the skattekonto account against Skatteverket's saldo, declared momsperioder whose VAT accounts were never moved to the settlement account, and bank transactions still unbooked past the BFNAR 2013:2 booking deadline. Each row has its status (ok, unexplained, cannot_check with a typed reason), the signed difference, the date it holds at, one Swedish sentence, what fixes it, and the explain call that names the items behind it. Both figures of every check come from the report a person sees. Read-only.
Use when: At the start of a session or before a close: what in the books does not add up, and what to do about it.
Don't use for: A single figure (GET /measures/{id}) or the full reconciliation of one account (GET /reconciliation/accounts/{accountKey}).
Pitfalls
- cannot_check is not ok: the check could not run (refusal_code says why: kontantmetoden, no bank balance, not connected to Skatteverket, ...).
- Live: every check reads today's state. The bank and skattekonto rows compare on the date the outside balance was fetched (as_of).
- difference is in unit: SEK, or a count for unbooked_bank_lines_overdue.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
as_of | string | |
counts | object | |
items | object[] | Unexplained first (largest SEK difference first), then cannot_check, then ok. |
truncated | number | Rows left out after the first 30 (ok rows are the last). |
Example response
{
"data": {
"as_of": "2026-10-16",
"counts": {
"unexplained": 1,
"cannot_check": 2,
"ok": 4
},
"items": [
{
"check_id": "receivables_vs_ledger",
"name_sv": "Kundreskontran mot bokförda kundfordringar",
"status": "unexplained",
"subject": {
"kind": "company"
},
"difference": 3000,
"unit": "SEK",
"as_of": "2026-10-16",
"summary_sv": "Kundreskontran visar 15 500,00 kr men de bokförda kundfordringarna är 12 500,00 kr: differens 3 000,00 kr.",
"refusal_code": null,
"next": {
"tool": "gnubok_link_invoice_to_voucher",
"screen": "/reports/kundreskontra"
},
"explain": {
"tool": "gnubok_explain_unexplained",
"arguments": {
"check_id": "receivables_vs_ledger"
},
"v1_path": "/api/v1/companies/9f1e…/unexplained/receivables_vs_ledger/explain"
}
}
],
"truncated": 0
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/unexplained/:checkId
unexplained.get · scope reports:read
One expected-zero check: its definition and basis, the figures it compares, and every result.
Runs one check and answers its definition (which figure minus which, read how), the rule it rests on, the tolerance, the reports the two figures come from, the action that fixes a difference, and one result per subject (the company, each bank account, each declared momsperiod) with both figures, the difference, a Swedish sentence, any refusal and the explain call.
Use when: An agent must say exactly what a queue row compares, or show both figures with where a person reads them.
Don't use for: The items behind a difference (GET /unexplained/{checkId}/explain).
Pitfalls
- An unknown check_id answers 404 UNEXPLAINED_CHECK_NOT_FOUND with the valid ids in details.valid.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
check_id | string | |
name_sv | string | |
name_en | string | |
definition | string | Which figure minus which, read how, and what zero means. |
definition_sv | string | |
basis | string | The rule it rests on: why the difference should be zero. |
unit | "SEK" | "count" | SEK for money differences; count for unbooked_bank_lines_overdue. |
tolerance | number | The largest difference that is still ok, in unit. |
subject_kind | "company" | "bank_account" | "vat_period" | |
compares | object[] | The figures compared, in the order of the subtraction. |
next_action | object | What fixes it. |
status | "ok" | "unexplained" | "cannot_check" | Worst of the results: unexplained, then cannot_check; ok only when every result is ok. |
results | object[] |
Example request
{
"check_id": "receivables_vs_ledger"
}
Example response
{
"data": {
"check_id": "receivables_vs_ledger",
"name_sv": "Kundreskontran mot bokförda kundfordringar",
"name_en": "Customer ledger against booked receivables",
"definition": "Open customer invoices in the invoice register now ... minus the booked balance of the receivables accounts ...",
"definition_sv": "Öppna kundfakturor i fakturaregistret just nu ... minus bokfört saldo på kundfordringskontona ...",
"basis": "Every balance sheet account is reconciled against its underlag ...",
"unit": "SEK",
"tolerance": 0.005,
"subject_kind": "company",
"compares": [
{
"report": "Kundreskontra (öppna kundfakturor)",
"v1_path": "/reports/ar-ledger",
"mcp_tool": "gnubok_get_ar_ledger",
"screen": "/reports/kundreskontra"
},
{
"report": "Huvudbok, kundfordringskontona",
"v1_path": "/reports/general-ledger",
"mcp_tool": "gnubok_get_general_ledger",
"screen": "/reports/huvudbok"
}
],
"next_action": {
"message_sv": "Öppna förklaringen ...",
"tool": "gnubok_link_invoice_to_voucher",
"screen": "/reports/kundreskontra"
},
"status": "unexplained",
"results": [
{
"status": "unexplained",
"subject": {
"kind": "company"
},
"difference": 3000,
"as_of": "2026-10-16",
"summary_sv": "Kundreskontran visar 15 500,00 kr men de bokförda kundfordringarna är 12 500,00 kr: differens 3 000,00 kr.",
"sides": [
{
"key": "register",
"label_sv": "Öppna kundfakturor i reskontran",
"label_en": "Open customer invoices in the register",
"amount": 15500,
"source": {
"report": "Kundreskontra (öppna kundfakturor)",
"v1_path": "/reports/ar-ledger",
"mcp_tool": "gnubok_get_ar_ledger",
"screen": "/reports/kundreskontra"
}
},
{
"key": "ledger",
"label_sv": "Bokförda kundfordringar",
"label_en": "Booked receivables",
"amount": 12500,
"source": {
"report": "Huvudbok, kundfordringskontona",
"v1_path": "/reports/general-ledger",
"mcp_tool": "gnubok_get_general_ledger",
"screen": "/reports/huvudbok"
}
}
],
"refusal": null,
"facts": {
"fiscal_period_id": "7c2b…"
},
"next_action": {
"message_sv": "Öppna förklaringen ...",
"tool": "gnubok_link_invoice_to_voucher",
"screen": "/reports/kundreskontra"
},
"explain": {
"tool": "gnubok_explain_unexplained",
"arguments": {
"check_id": "receivables_vs_ledger"
},
"v1_path": "/api/v1/companies/9f1e…/unexplained/receivables_vs_ledger/explain"
}
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/unexplained/:checkId/explain
unexplained.explain · scope reports:read
What a difference in the queue consists of: its parts and the items behind them, with typed handles.
Explains one result of a check: the same difference as the queue, broken into parts (open invoices without a verifikat and verifikat on the control account without an invoice; unbooked bank transactions; Skatteverket events missing from the books and verifikat Skatteverket does not know; the VAT accounts not yet cleared; the overdue bank rows) and, paged, the items behind each part with their typed handles (inv_, sinv_, je_, txn_). residual is what the items do not name and note says what it usually is. For a check with several results (bank accounts, momsperioder), send the subject: cash_account_id, or period_type, year and period, as the explain handle of the row gives them.
Use when: An agent must show why a queue row is not zero, or find the records to fix.
Don't use for: The queue itself (GET /unexplained).
Pitfalls
- items is paged: limit (1-200, default 25) and offset; next_offset is null on the last page.
- A check with several results and no subject answers 400 UNEXPLAINED_SUBJECT_REQUIRED with the subjects in details.subjects.
- cash_account_id on a company-wide check answers 400 UNEXPLAINED_PARAMETER_NOT_ACCEPTED.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
cash_account_id | string | no | bank_balance_vs_bank: the bank account (cash account id or cash_ handle). |
period_type | "monthly" | "quarterly" | "yearly" | no | vat_declared_not_settled: the momsperiod length. |
year | number | no | vat_declared_not_settled: year of the momsperiod. |
period | number | no | vat_declared_not_settled: 1-12 monthly, 1-4 quarterly, 1 yearly. |
limit | number | no | Item page size, 1-200 (default 25). |
offset | number | no | Items to skip (next_offset of the previous page). |
Response fields
| Name | Type | Description |
|---|---|---|
check_id | string | |
name_sv | string | |
status | "ok" | "unexplained" | "cannot_check" | ok: within the tolerance. unexplained: a difference to resolve. cannot_check: not run, refusal says why; never read it as ok. |
subject | object | What the result is about: the company, one bank account, or one momsperiod. |
difference | number | null | |
unit | "SEK" | "count" | SEK for money differences; count for unbooked_bank_lines_overdue. |
as_of | string | |
summary_sv | string | |
sides | object[] | |
refusal | object | null | Why the check could not run. |
parts | object[] | The difference broken into parts, each with its items. |
residual | number | null | Difference minus the parts: what the items do not name. |
reconciles | boolean | True when the parts account for the difference within the tolerance. |
note | string | null | What else bears on it, in English. |
items | object | |
next_action | object | null | What fixes it. |
Example request
{
"check_id": "receivables_vs_ledger"
}
Example response
{
"data": {
"check_id": "receivables_vs_ledger",
"name_sv": "Kundreskontran mot bokförda kundfordringar",
"status": "unexplained",
"subject": {
"kind": "company"
},
"difference": 3000,
"unit": "SEK",
"as_of": "2026-10-16",
"summary_sv": "Kundreskontran visar 15 500,00 kr men de bokförda kundfordringarna är 12 500,00 kr: differens 3 000,00 kr.",
"sides": [],
"refusal": null,
"parts": [
{
"key": "register_without_voucher",
"label_sv": "Öppna kundfakturor utan verifikat",
"label_en": "Open customer invoices without a verifikat",
"amount": 3000,
"count": 1
},
{
"key": "ledger_without_invoice",
"label_sv": "Verifikat på kundfordringskontona utan faktura i reskontran",
"label_en": "Vouchers on the receivables accounts without an invoice in the register",
"amount": 0,
"count": 0
}
],
"residual": 0,
"reconciles": true,
"note": null,
"items": {
"list": [
{
"part": "register_without_voucher",
"item_type": "invoice",
"invoice_id": "a1b2…",
"handle": "inv_a1b2…",
"date": "2026-10-01",
"label": "Faktura 1002, Beta HB",
"amount": 3000,
"currency": "SEK"
}
],
"total": 1,
"offset": 0,
"next_offset": null
},
"next_action": {
"message_sv": "Öppna förklaringen ...",
"tool": "gnubok_link_invoice_to_voucher",
"screen": "/reports/kundreskontra"
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}