Menu

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

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

NameTypeDescription
as_ofstring
countsobject
itemsobject[]Unexplained first (largest SEK difference first), then cannot_check, then ok.
truncatednumberRows 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

NameTypeDescription
check_idstring
name_svstring
name_enstring
definitionstringWhich figure minus which, read how, and what zero means.
definition_svstring
basisstringThe rule it rests on: why the difference should be zero.
unit"SEK" | "count"SEK for money differences; count for unbooked_bank_lines_overdue.
tolerancenumberThe largest difference that is still ok, in unit.
subject_kind"company" | "bank_account" | "vat_period"
comparesobject[]The figures compared, in the order of the subtraction.
next_actionobjectWhat fixes it.
status"ok" | "unexplained" | "cannot_check"Worst of the results: unexplained, then cannot_check; ok only when every result is ok.
resultsobject[]

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

NameTypeRequiredDescription
cash_account_idstringnobank_balance_vs_bank: the bank account (cash account id or cash_ handle).
period_type"monthly" | "quarterly" | "yearly"novat_declared_not_settled: the momsperiod length.
yearnumbernovat_declared_not_settled: year of the momsperiod.
periodnumbernovat_declared_not_settled: 1-12 monthly, 1-4 quarterly, 1 yearly.
limitnumbernoItem page size, 1-200 (default 25).
offsetnumbernoItems to skip (next_offset of the previous page).

Response fields

NameTypeDescription
check_idstring
name_svstring
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.
subjectobjectWhat the result is about: the company, one bank account, or one momsperiod.
differencenumber | null
unit"SEK" | "count"SEK for money differences; count for unbooked_bank_lines_overdue.
as_ofstring
summary_svstring
sidesobject[]
refusalobject | nullWhy the check could not run.
partsobject[]The difference broken into parts, each with its items.
residualnumber | nullDifference minus the parts: what the items do not name.
reconcilesbooleanTrue when the parts account for the difference within the tolerance.
notestring | nullWhat else bears on it, in English.
itemsobject
next_actionobject | nullWhat 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"
  }
}