Menu
Cookbooks
API reference
- Overview
- Companies
- Portfolio
- Customers
- Invoices
- Articles
- Suppliers
- Supplier invoices
- Supplier payment files
- Expense claims
- Transactions
- Reconciliation
- Bank accounts
- Journal entries
- Voucher gap explanations
- Fiscal periods
- Accounts
- Fixed assets
- Documents
- Inbox items
- Dimensions
- Employees
- Salary runs
- Reports
- Imports
- Compliance check
- Skatteverket
- Peppol
- Webhooks
- Operations
- Health
- API key
Fiscal periods
Fiscal years and their lifecycle: create, edit, lock, unlock, close, year-end, opening balances, FX revaluation, and klarmarkering of years closed in a previous system.
Endpoints
GET/api/v1/companies/:companyId/fiscal-periods: List fiscal periods (räkenskapsår).GET/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/ixbrl: The K2 årsredovisning as an inline XBRL (XHTML) document.GET/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/ixbrl/validate: Pre-flight the generated iXBRL årsredovisning against Bolagsverket's kontrollera rules.GET/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/pdf: The årsredovisning as PDF: the live draft or a frozen version.GET/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures: The årsredovisning signer roster and its signatures.GET/api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/split-per-project: Preview splitting a year's ingående balanser per project from the previous year's tagged closing balances.POST/api/v1/companies/:companyId/fiscal-periods: Create a fiscal year (räkenskapsår).POST/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/narrative: Edit the årsredovisning texts: förvaltningsberättelse, resultatdisposition, disclosure notes and K3 note texts.POST/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures: Add a board member or the VD to the årsredovisning signer roster.POST/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/versions: Freeze an immutable årsredovisning version: a draft snapshot, or the version that gets signed.POST/api/v1/companies/:companyId/fiscal-periods/:id/close: Close a fiscal period (IRREVERSIBLE per BFL 5 kap 8 §).POST/api/v1/companies/:companyId/fiscal-periods/:id/close-external: Mark a migrated fiscal year as closed in the previous system (klarmarkera).POST/api/v1/companies/:companyId/fiscal-periods/:id/currency-revaluation: Run FX revaluation for the fiscal period.POST/api/v1/companies/:companyId/fiscal-periods/:id/lock: Lock a fiscal period (no new entries can be posted into it).POST/api/v1/companies/:companyId/fiscal-periods/:id/opening-balances: Generate opening-balance verifikation for the next fiscal period.POST/api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/correct: Correct a year's ingående balanser by storno: the full corrected IB replaces the old one.POST/api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/manual: Book a fiscal year's ingående balanser (IB) from explicit lines, for a company new to Accounted.POST/api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/split-per-project: Split a year's ingående balanser per project, inside the same IB verifikat (inline rättelse).POST/api/v1/companies/:companyId/fiscal-periods/:id/reopen-external: Undo klarmarkera: reopen a year marked closed in the previous system.POST/api/v1/companies/:companyId/fiscal-periods/:id/unlock: Unlock a locked (not closed) fiscal year.POST/api/v1/companies/:companyId/fiscal-periods/:id/year-end: Execute year-end closing (currency revaluation + closing entry).PATCH/api/v1/companies/:companyId/fiscal-periods/:id: Rename or re-date an open fiscal year.PATCH/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/compliance: Answer the årsredovisning compliance questions and record the confirmations.PATCH/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures/:signatureId: Record that a signer signed the frozen årsredovisning version, or declined.DELETE/api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures/:signatureId: Remove a signer from the årsredovisning roster before a version binds it.
GET /api/v1/companies/:companyId/fiscal-periods
fiscal-periods.list · scope reports:read
List fiscal periods (räkenskapsår).
Returns every fiscal period for the company ordered by period_start DESC. is_closed=true means bokslut has been signed; locked_at non-null means writes are blocked at the DB-trigger level.
Use when: You need to find the active period before booking, build a year-selector UI, or audit the period-lock history.
Don't use for: Creating, editing, locking or closing periods: use POST /fiscal-periods, PATCH /fiscal-periods/{id}, POST /fiscal-periods/{id}/lock, /unlock, /close and /year-end.
Pitfalls
- previous_period_id chains the bokslut continuity (BFNAR 2013:2). A null value on a non-first period is a data-quality red flag.
- A period can be locked but not closed (löpande bokföring of the new year while bokslut work continues on the prior year: see BFL 5 kap 2 § for the löpande bokföring deadline).
- BFL 3 kap caps a single fiscal period at 18 months. First-year exceptions are allowed.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type |
|---|---|
fiscal_periods[].id | string |
fiscal_periods[].name | string |
fiscal_periods[].period_start | string |
fiscal_periods[].period_end | string |
fiscal_periods[].is_closed | boolean |
fiscal_periods[].closed_at | string | null |
fiscal_periods[].locked_at | string | null |
fiscal_periods[].previous_period_id | string | null |
fiscal_periods[].created_at | string |
fiscal_periods[].duration_days | number |
fiscal_periods[].exceeds_18_months | boolean |
Example response
{
"data": {
"fiscal_periods": [
{
"id": "fp_2026",
"name": "Räkenskapsår 2026",
"period_start": "2026-01-01",
"period_end": "2026-12-31",
"is_closed": false,
"locked_at": null
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/ixbrl
arsredovisning.ixbrl · scope reports:read
The K2 årsredovisning as an inline XBRL (XHTML) document.
Generates the iXBRL document Bolagsverket's digital filing takes (K2, Bolagsverket taxonomy), for the live draft or a frozen version (version_id). The XHTML is also the human-readable document. It is not a filing: digital filing goes through connected software with the fastställelseintyg signed by BankID, which this API does not do.
Use when: Archiving the digital document, or validating it with external tools.
Don't use for: The pre-flight result as JSON (GET .../arsredovisning/ixbrl/validate) or a printable copy (GET .../arsredovisning/pdf).
Pitfalls
- K2 aktiebolag only.
- The live draft is refused while an SIE import is unfinished (409); a frozen version stays readable.
- proposed_dividend (whole SEK) applies to the live draft only.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
version_id | string | no | A frozen version (annual_report_version_id); omit for the live draft. |
proposed_dividend | number | no | Live draft only: proposed dividend in whole SEK for the resultatdisposition. |
Example response
{
"_note": "Returns application/xhtml+xml as a binary download."
}
GET /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/ixbrl/validate
arsredovisning.validate-ixbrl · scope reports:read
Pre-flight the generated iXBRL årsredovisning against Bolagsverket's kontrollera rules.
Generates the K2 inline XBRL document (the live draft, or a frozen version with version_id) and runs the local mirror of Bolagsverket's kontrollera checks on it, plus a generation dry run and the 5 MB size limit. Issues carry the Bolagsverket code where one exists (e.g. 1107 missing signers) or ACC-*. Nothing is sent to Bolagsverket. Read-only.
Use when: Before a digital filing, or to see why the iXBRL document is not ready.
Don't use for: The ÅRL/K2 completeness checks per stage (gnubok_validate_arsredovisning), or the document itself (GET .../arsredovisning/ixbrl, v1 only).
Pitfalls
- K2 aktiebolag only: the iXBRL generator does not produce K3 documents.
- A validation with issues still answers 200; read ok and error_count.
- An unknown version_id answers 404 NOT_FOUND.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
version_id | string | no | A frozen version (annual_report_version_id); omit for the live draft. |
proposed_dividend | number | no | Live draft only: proposed dividend in whole SEK for the resultatdisposition. |
Response fields
| Name | Type |
|---|---|
ok | boolean |
issues | object[] |
error_count | number |
warning_count | number |
generated_bytes | number |
entry_point | string |
period | object |
annual_report_version_id | string | null |
Example request
{
"fiscal_period_id": "7c2b…"
}
Example response
{
"data": {
"ok": false,
"issues": [
{
"code": "1107",
"severity": "error",
"message": "Underskrifter saknas."
}
],
"error_count": 1,
"warning_count": 0,
"generated_bytes": 84211,
"entry_point": "k2-ab-risbs-2024-09-12",
"period": {
"start": "2026-01-01",
"end": "2026-12-31"
},
"annual_report_version_id": null
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/pdf
arsredovisning.pdf · scope reports:read
The årsredovisning as PDF: the live draft or a frozen version.
Renders the årsredovisning for the räkenskapsår (K2 or K3 template by the framework) as the dashboard does. Without version_id it is the live draft from the books, named ...-utkast.pdf; with version_id it is that immutable version with the signatures recorded on it, named ...-papperskopia.pdf once signed. Nothing is sent to Bolagsverket.
Use when: A printable copy for the board to sign on paper, the archive, or a review.
Don't use for: The inline XBRL document for digital filing (GET .../arsredovisning/ixbrl) or the report as JSON (gnubok_preview_arsredovisning).
Pitfalls
- The live draft is refused while an SIE import is unfinished (409); a frozen version stays readable.
- An unknown version_id answers 404 NOT_FOUND.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
version_id | string | no | A frozen version (annual_report_version_id); omit for the live draft. |
Example response
{
"_note": "Returns application/pdf as a binary download."
}
GET /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures
arsredovisning.list-signatories · scope reports:read
The årsredovisning signer roster and its signatures.
The current signer slots for the räkenskapsår: the unbound pending roster while one exists, otherwise the slots bound to the latest version with their status, signing date, method and evidence reference. The signature_id values are what PATCH and DELETE .../signatures/{signatureId} take; finalizing a version may create new slots bound to it. Read-only.
Use when: Before adding a signer (avoid duplicates) or to find the slot to record a signature on after a version is finalized.
Don't use for: The report content (gnubok_preview_arsredovisning) or the version list (gnubok_list_arsredovisning_versions).
Pitfalls
- Declined slots are listed too; they do not appear in the document.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
signatures[].signature_id | string | |
signatures[].fiscal_period_id | string | |
signatures[].annual_report_version_id | string | null | Null while the slot is on the unbound roster. |
signatures[].role | string | |
signatures[].signer_name | string | |
signatures[].status | "pending" | "signed" | "declined" | |
signatures[].signed_at | string | null | |
signatures[].signing_method | "paper_original" | "advanced_e_signature" | "bankid" | "bolagsverket" | null | |
signatures[].evidence_reference | string | null | |
signatures[].evidence_recorded_at | string | null | |
signatures[].created_at | string |
Example response
{
"data": {
"signatures": [
{
"signature_id": "5b1c…",
"fiscal_period_id": "7c2b…",
"annual_report_version_id": null,
"role": "Styrelseledamot",
"signer_name": "Anna Andersson",
"status": "pending",
"signed_at": null,
"signing_method": null,
"evidence_reference": null,
"evidence_recorded_at": null,
"created_at": "2027-03-02T09:00:00Z"
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/split-per-project
opening-balances.split-per-project-preview · scope reports:read
Preview splitting a year's ingående balanser per project from the previous year's tagged closing balances.
For the year's IB verifikat, computes the per-project split of each balance sheet account from the previous fiscal year's closing balances per object of the dimensions that carry across years (registry resets_annually = false: projekt, dimension 6). Each such account becomes one line per project plus one untagged remainder (the current IB minus the projects, any sign); the account total never changes. Accounts without a project balance last year, and the VAT accounts (26xx), stay untouched. Answers current and proposed lines per account, which accounts change, unresolved project codes, and blocked (code and Swedish message) when the year is closed, locked, behind the lock date or has a bokslut. Read-only.
Use when: A project-filtered ledger opens at zero for a year whose IB was booked before project balances were carried (an IB imported or closed earlier), and the user asks how project balances carry into the new year.
Don't use for: Changing amounts in the IB (POST /fiscal-periods/{id}/opening-balances/correct) or a year closed after project balances were carried: the year-end already splits the IB per project.
Pitfalls
- The basis is the previous year in Accounted as it stands: if that year is still open (source_period_closed false), later bookings there change the split.
- A previous year whose own IB was untagged only carries the projects' movements of that year.
- An account whose IB already carries a different project split (an IB imported with SIE #OIB, or split by hand) is skipped with skip_reason existing_split, never overwritten.
- unresolved_dimensions lists codes missing from the registry: the apply refuses until they exist.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
fiscal_period_id | string | |
fiscal_period_name | string | null | |
journal_entry_id | string | null | The IB verifikat the split corrects in place. |
voucher | string | null | |
source | "previous_year" | |
source_fiscal_period_id | string | null | |
source_fiscal_period_name | string | null | |
source_period_closed | boolean | null | False while the previous year is still open: its balances may still move. |
accumulating_dimensions | string[] | SIE numbers of the dimensions whose balances carry (resets_annually = false), e.g. ["6"]. |
method | "inline_rattelse" | |
accounts_to_change | number | |
accounts_unchanged | number | |
accounts_skipped | number | |
changed_amount_sek | number | What the split moves in SEK: the largest side (debit or credit) of the lines struck or added. Priced by an API key's unattended-commit ceiling. |
can_apply | boolean | |
blocked | object | null | Why the split cannot run now: the code the apply answers, with the Swedish message. |
unresolved_dimensions | object[] | |
dimension_values | object[] | |
accounts | object[] | |
fingerprint | string | Pass as expected_fingerprint to apply exactly this split. |
Example response
{
"data": {
"fiscal_period_id": "7b3a…",
"fiscal_period_name": "2026",
"journal_entry_id": "4d2a…",
"voucher": "A1",
"source": "previous_year",
"source_fiscal_period_id": "5e91…",
"source_fiscal_period_name": "2025",
"source_period_closed": true,
"accumulating_dimensions": [
"6"
],
"method": "inline_rattelse",
"accounts_to_change": 1,
"accounts_unchanged": 0,
"accounts_skipped": 0,
"changed_amount_sek": 1800,
"can_apply": true,
"blocked": null,
"unresolved_dimensions": [],
"dimension_values": [
{
"sie_dim_no": "6",
"code": "P1",
"name": "Kv. Eken",
"is_active": true
}
],
"accounts": [
{
"account_number": "1470",
"account_name": "Pågående arbeten",
"total": 1800,
"status": "change",
"skip_reason": null,
"current_lines": [
{
"journal_entry_line_id": "9c0f…",
"debit_amount": 1800,
"credit_amount": 0,
"amount": 1800,
"dimensions": {},
"line_description": "IB 1470"
}
],
"proposed_lines": [
{
"debit_amount": 1300,
"credit_amount": 0,
"amount": 1300,
"dimensions": {
"6": "P1"
},
"line_description": "Ingående balans: Pågående arbeten"
},
{
"debit_amount": 500,
"credit_amount": 0,
"amount": 500,
"dimensions": {},
"line_description": "Ingående balans: Pågående arbeten"
}
]
}
],
"fingerprint": "3f9a…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods
fiscal-periods.create · scope bookkeeping:write
Create a fiscal year (räkenskapsår).
Creates a räkenskapsår and links it into the continuity chain (previous_period_id, BFNAR 2013:2). The year must be at most 18 months, end on a month end, and start on the 1st unless it becomes the earliest year (BFL 3 kap. 1 and 3 §§). It must be contiguous with its neighbours: appended, it starts the day after the latest year ends; filling a gap, it also ends the day before the next year starts; prepended, it ends the day before the earliest year starts. A still-open prior year does not block: the 201 carries a PRIOR_FISCAL_YEAR_STILL_OPEN warning. Idempotent. Dry-runnable.
Use when: The company needs the next räkenskapsår to book in (e.g. January arrives), or an earlier year must exist before its SIE file or opening balances can be imported.
Don't use for: Closing the prior year (run the year-end), or changing an existing year (PATCH /fiscal-periods/{id}).
Pitfalls
- A start that does not continue the preceding year answers 400 FISCAL_PERIOD_NOT_CONTIGUOUS with details.expected_start (or details.expected_end): retry with that date.
- Overlapping an existing year answers 409 FISCAL_PERIOD_OVERLAP.
- An enskild firma normally runs the calendar year; a brutet räkenskapsår needs Skatteverket's permission.
- There is no delete: a wrong year can be re-dated with PATCH only while nothing is posted in it.
Risk: medium · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name, e.g. "Räkenskapsår 2027". |
period_start | string | yes | First day, YYYY-MM-DD. |
period_end | string | yes | Last day, YYYY-MM-DD (a month end). |
Response fields
| Name | Type |
|---|---|
fiscal_period | object |
Example request
{
"name": "Räkenskapsår 2027",
"period_start": "2027-01-01",
"period_end": "2027-12-31"
}
Example response
{
"data": {
"fiscal_period": {
"id": "a8f1…",
"name": "Räkenskapsår 2027",
"period_start": "2027-01-01",
"period_end": "2027-12-31",
"is_closed": false,
"closed_at": null,
"closed_externally": false,
"locked_at": null,
"previous_period_id": "5c2e…",
"created_at": "2026-12-01T09:00:00Z"
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/narrative
arsredovisning.update-narrative · scope bookkeeping:write
Edit the årsredovisning texts: förvaltningsberättelse, resultatdisposition, disclosure notes and K3 note texts.
Saves the narrative overrides for a räkenskapsår's årsredovisning, as the dashboard's text step does. Only the fields sent change; null clears a field back to the generated text. Covers the förvaltningsberättelse (description, important_events), the resultatdisposition text and proposed_dividend, the årsstämma date and decision, the ÅRL 5 kap. disclosures (long-term debt, ställda säkerheter, eventualförpliktelser), koncernförhållanden, a manual medelantal anställda, K3 note texts (note_overrides replaces the whole object) and the K3 kassaflödesanalys omission. A save clears the narrative confirmation in the compliance profile. Allowed after the period is closed; refused once a Bolagsverket submission is registrerad. Idempotent. Dry-runnable.
Use when: Writing or correcting the texts of the årsredovisning before a version is frozen for signing.
Don't use for: The compliance profile answers (PATCH .../arsredovisning/compliance), the figures (they come from the books), or freezing the document (POST .../arsredovisning/versions).
Pitfalls
- A version already frozen keeps its text: freeze a new version after editing.
- agm_disposition_outcome alternative_decision needs agm_disposition_decision text (400 VALIDATION_ERROR).
- parent_company_org_number is an organisationsnummer or a foreign registration id; a personnummer is refused.
- Registered at Bolagsverket: 409 ARSREDOVISNING_REGISTERED.
- Saving clears narrative_confirmed: confirm again with PATCH .../compliance {"narrative_confirmed": true}.
Risk: medium · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
description | string | null | no | Förvaltningsberättelse: allmänt om verksamheten. |
important_events | string | null | no | Förvaltningsberättelse: väsentliga händelser under räkenskapsåret. |
resultatdisposition | string | null | no | Styrelsens förslag till resultatdisposition, as text. |
proposed_dividend | number | null | no | Föreslagen utdelning in SEK (rounded to öre); null clears it. |
agm_date | string | null | no | Årsstämmans datum, YYYY-MM-DD (fills the fastställelseintyg). |
long_term_debt_over_five_years | number | null | no | ÅRL 5:13 §: long-term debt falling due after more than five years, SEK. |
securities_pledged | string | null | no | ÅRL 5:14 §: ställda säkerheter. |
contingent_liabilities | string | null | no | ÅRL 5:15 §: eventualförpliktelser. |
parent_company_name | string | null | no | Moderföretagets namn (koncernförhållanden). |
parent_company_org_number | "" | string | null | no | Moderföretagets organisationsnummer or foreign registration id; never a personnummer. |
parent_company_city | string | null | no | Moderföretagets säte. |
medelantal_anstallda_override | number | null | no | ÅRL 5:20 §: medelantal anställda as a whole number; null uses the computed average. |
member_count_change | string | null | no | ÅRL 6 kap. 3 § (ekonomisk förening): change in the number of members during the year. |
insatser_repayable_next_year | number | null | no | ÅRL 6 kap. 3 § (ekonomisk förening): insatser to repay next fiscal year, SEK; 0 when none. |
forlagsinsatser_dividend_right | string | null | no | ÅRL 6 kap. 3 § (ekonomisk förening): the distribution right förlagsinsatser carry. |
forlagsinsatser_redeemable_two_years | number | null | no | ÅRL 6 kap. 3 § (ekonomisk förening): förlagsinsatser redeemable within two years, SEK; 0 when none. |
long_term_debt_over_five_years_confirmed | boolean | no | |
securities_pledged_confirmed | boolean | no | |
contingent_liabilities_confirmed | boolean | no | |
parent_company_confirmed | boolean | no | |
agm_disposition_outcome | "proposal_approved" | "alternative_decision" | null | no | What the årsstämma decided about the resultatdisposition. |
agm_disposition_decision | string | null | no | The årsstämma's alternative decision, required with alternative_decision. |
note_overrides | object | no | K3 note texts by note key; the whole object is replaced, blank or null resets a note. |
omit_kassaflodesanalys | boolean | no | |
kassaflodesanalys_omission_confirmed | boolean | no |
Response fields
| Name | Type |
|---|---|
narrative_id | string |
fiscal_period_id | string |
description | string | null |
important_events | string | null |
resultatdisposition | string | null |
proposed_dividend | number | null |
agm_date | string | null |
long_term_debt_over_five_years | number | null |
securities_pledged | string | null |
contingent_liabilities | string | null |
parent_company_name | string | null |
parent_company_org_number | string | null |
parent_company_city | string | null |
medelantal_anstallda_override | number | null |
agm_disposition_outcome | "proposal_approved" | "alternative_decision" | null |
agm_disposition_decision | string | null |
note_overrides | object |
omit_kassaflodesanalys | boolean |
updated_at | string |
Example request
{
"description": "Bolaget bedriver konsultverksamhet inom IT.",
"agm_date": "2027-05-20"
}
Example response
{
"data": {
"narrative_id": "1f0e…",
"fiscal_period_id": "7c2b…",
"description": "Bolaget bedriver konsultverksamhet inom IT.",
"agm_date": "2027-05-20",
"updated_at": "2027-03-02T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures
arsredovisning.add-signatory · scope bookkeeping:write
Add a board member or the VD to the årsredovisning signer roster.
Adds a pending signer slot (role and name) to the roster for the räkenskapsår. ÅRL 2 kap. 7 § requires every ordinarie styrelseledamot and the VD, if any, to sign. This records who is to sign; it signs nothing and involves no BankID. Any roster change clears the signer roster confirmation. Answers 201 with the slot. Idempotent. Dry-runnable.
Use when: Setting up who signs, before finalizing a version (POST .../arsredovisning/versions action finalize).
Don't use for: Recording that someone signed (PATCH .../signatures/{signatureId}) or filing with Bolagsverket.
Pitfalls
- The same role and name twice on the unbound roster answers 409 ARSREDOVISNING_SIGNER_ALREADY_EXISTS.
- signer_name is the name as registered at Bolagsverket; a personnummer is refused (400).
- Confirm the roster afterwards: PATCH .../compliance {"signer_roster_confirmed": true}.
Risk: medium · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
role | "Styrelseledamot" | "Styrelseordförande" | "VD" | "Verkställande direktör" | yes | Styrelseledamot, Styrelseordförande, VD or Verkställande direktör. |
signer_name | string | yes | Full name as registered at Bolagsverket. Never a personnummer. |
Response fields
| Name | Type | Description |
|---|---|---|
signature_id | string | |
fiscal_period_id | string | |
annual_report_version_id | string | null | Null while the slot is on the unbound roster. |
role | string | |
signer_name | string | |
status | "pending" | "signed" | "declined" | |
signed_at | string | null | |
signing_method | "paper_original" | "advanced_e_signature" | "bankid" | "bolagsverket" | null | |
evidence_reference | string | null | |
evidence_recorded_at | string | null | |
created_at | string |
Example request
{
"role": "Styrelseledamot",
"signer_name": "Anna Andersson"
}
Example response
{
"data": {
"signature_id": "5b1c…",
"fiscal_period_id": "7c2b…",
"annual_report_version_id": null,
"role": "Styrelseledamot",
"signer_name": "Anna Andersson",
"status": "pending",
"signed_at": null,
"signing_method": null,
"evidence_reference": null,
"evidence_recorded_at": null,
"created_at": "2027-03-02T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/versions
arsredovisning.create-version · scope bookkeeping:write
Freeze an immutable årsredovisning version: a draft snapshot, or the version that gets signed.
Builds the årsredovisning from a complete read of the books (refused while an SIE import is unfinished) and stores it as an immutable version with its content hash. action snapshot stores a draft; action finalize requires every signing-stage check to pass, marks the version ready_for_signature, supersedes an earlier ready or signed version and binds the signer roster to it. certificate_signer names who signs the fastställelseintyg. expected_content_hash (from a dry run) makes the call refuse when the content has changed since. Answers 201 with the version. Idempotent. Dry-runnable: the dry run answers the content hash and the validation counts.
Use when: The texts, compliance answers and signer roster are done and the document is to be signed (finalize), or a checkpoint of the draft is wanted (snapshot).
Don't use for: Filing with Bolagsverket (dashboard only, BankID), recording signatures (PATCH .../signatures/{signatureId}) or reading the document (GET .../arsredovisning/pdf).
Pitfalls
- Statements that do not tie, or (finalize) any signing-stage error, answer 409 ARSREDOVISNING_INCOMPLETE with the validation in details: run gnubok_validate_arsredovisning with stage signing first.
- finalize needs a confirmed signer roster (PATCH .../compliance {"signer_roster_confirmed": true}) and at least one signer.
- Content changed since the hash you pass: 409 ARSREDOVISNING_CONTENT_CHANGED; dry-run again and review.
- A new finalize supersedes the previous ready_for_signature version; signatures recorded on it do not carry over.
- Refused while an SIE import is unfinished: complete or undo it first.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
action | "snapshot" | "finalize" | yes | snapshot saves a draft version; finalize freezes the version for signing (ready_for_signature). |
certificate_signer | object | no | Who signs the fastställelseintyg (a board member or the VD). |
expected_content_hash | string | no | The content_hash a dry run answered; the version is refused when the content no longer matches. |
Response fields
| Name | Type | Description |
|---|---|---|
annual_report_version_id | string | |
version_number | number | |
status | "draft" | "ready_for_signature" | "signed" | "filed" | "registered" | "superseded" | |
framework | string | |
content_hash | string | SHA-256 of the frozen content (signature dates excluded). |
taxonomy_version | string | null | |
entry_point | string | null | |
finalized_at | string | null | |
created_at | string |
Example request
{
"action": "finalize",
"certificate_signer": {
"first_name": "Anna",
"last_name": "Andersson",
"role": "Styrelseledamot"
}
}
Example response
{
"data": {
"annual_report_version_id": "4e7d…",
"version_number": 2,
"status": "ready_for_signature",
"framework": "k2",
"content_hash": "9f86d081…",
"taxonomy_version": "2024-09-12",
"entry_point": "k2-ab-risbs-2024-09-12",
"finalized_at": "2027-03-02T09:00:00Z",
"created_at": "2027-03-02T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/close
fiscal-periods.close · scope bookkeeping:write
Close a fiscal period (IRREVERSIBLE per BFL 5 kap 8 §).
Sets is_closed=true + closed_at on the period. Pre-requisites: period must be locked (call /lock first) AND year-end closing must have been executed (call /year-end first). Sync. The DB blocks any subsequent JE inserts.
Use when: Final step in the year-end flow: lock → year-end → close. Closing freezes the period for BFL 7 kap retention.
Don't use for: Locking a period (use /lock). Running the year-end closing entry (use /year-end). UNDOING a close (not supported, irreversible).
Pitfalls
- Idempotency-Key is mandatory.
- IRREVERSIBLE. Once is_closed=true, the period is read-only forever (BFL 5 kap 8 § + 7 kap).
- Pre-conditions: locked + closing_entry_id present. Otherwise the call returns CONFLICT.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type |
|---|---|
id | string |
is_closed | true |
closed_at | string |
Example response
{
"data": {
"id": "a8f1…",
"is_closed": true,
"closed_at": "2026-05-12T14:30:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/close-external
fiscal-periods.close-external · scope bookkeeping:write
Mark a migrated fiscal year as closed in the previous system (klarmarkera).
Closes and locks an imported historical räkenskapsår whose bokslut was done in the previous bookkeeping software, without a closing entry here, and writes the decision to the audit log. Only for migrated years: the year must have ended, have no closing entry in Accounted, and hold imported verifikat, no verifikat, or balance-sheet-only verifikat with the next year's IB already posted. Unbooked bank transactions in the year block it, as for a lock. Undo with reopen-external. Idempotent. Dry-runnable.
Use when: After an SIE migration, the earlier years show as pending bokslut although their bokslut was done in the old system.
Don't use for: Closing a year bookkept in Accounted: run the year-end (POST /fiscal-periods/{id}/year-end), which transfers the result and carries the balances forward.
Pitfalls
- A year bookkept here with result accounts answers 409 FISCAL_PERIOD_CLOSE_EXTERNAL_NATIVE_BOOKKEEPING: run the year-end instead.
- A running year answers 409 FISCAL_PERIOD_CLOSE_EXTERNAL_NOT_ENDED.
- Unbooked bank transactions answer 400 PERIOD_HAS_UNBOOKED_TRANSACTIONS with the count in details.reason.
Risk: high · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
id | string |
name | string |
period_start | string |
period_end | string |
is_closed | boolean |
closed_at | string | null |
closed_externally | boolean |
locked_at | string | null |
previous_period_id | string | null |
created_at | string |
Example response
{
"data": {
"id": "a8f1…",
"name": "Räkenskapsår 2024",
"period_start": "2024-01-01",
"period_end": "2024-12-31",
"is_closed": true,
"closed_at": "2026-09-25T09:00:00Z",
"closed_externally": true,
"locked_at": "2026-09-25T09:00:00Z",
"previous_period_id": "5c2e…",
"created_at": "2026-12-01T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/currency-revaluation
fiscal-periods.currency-revaluation · scope bookkeeping:write
Run FX revaluation for the fiscal period.
Re-rates open foreign-currency AR (1510) and AP (2440) at the closing date's Riksbanken rate and posts the SEK delta to 3960 (valutakursvinst) / 7960 (valutakursförlust). Returns 202 with operation_id. Idempotent per-period: the engine throws if a revaluation has already been posted for the same fiscal_period_id.
Use when: Before /year-end if your books have open foreign-currency receivables or payables. /year-end also runs this internally, so you only need to call it separately when you want the FX-only entry without the full closing.
Don't use for: Re-running on the same period (CURRENCY_REVALUATION_ALREADY_EXISTS). Revaluing a closed period (the trigger blocks JE writes to closed periods).
Pitfalls
- Idempotency-Key is mandatory.
- Engine returns null if no open foreign-currency items exist: the operation succeeds with result.revaluation_entry_id=null.
- as_of_date defaults to period_end if omitted.
Risk: high · Idempotent: yes · Reversible: yes · Dry-run supported: no
Request body
| Name | Type | Required |
|---|---|---|
as_of_date | string | no |
Response fields
| Name | Type |
|---|---|
operation_id | string |
type | "fiscal_periods.currency_revaluation" |
status | "queued" | "running" | "succeeded" | "failed" |
poll_url | string |
webhook_event | "operation.completed" |
Example request
{
"as_of_date": "2026-12-31"
}
Example response
{
"data": {
"operation_id": "0e9c…",
"type": "fiscal_periods.currency_revaluation",
"status": "succeeded",
"poll_url": "/api/v1/operations/0e9c…",
"webhook_event": "operation.completed"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/lock
fiscal-periods.lock · scope bookkeeping:write
Lock a fiscal period (no new entries can be posted into it).
Sets locked_at on the period. Refuses if uncategorised business transactions remain in the period: they must be bokfört first. The DB trigger blocks JE inserts into locked periods; locking is the application-level pre-step before /close. Sync.
Use when: Finishing a period and you want to stop new postings. Step 1 of a three-step year-end flow: lock → year-end → close.
Don't use for: Locking an already-closed period (no-op). Bypassing the uncategorised-transactions guard: categorise or mark-private first.
Pitfalls
- Idempotency-Key is mandatory.
- A period with uncategorised business transactions cannot be locked; the response surfaces the count.
- Locking is reversible until /close: POST /fiscal-periods/{id}/unlock lifts it.
Risk: high · Idempotent: yes · Reversible: yes · Dry-run supported: no
Response fields
| Name | Type |
|---|---|
id | string |
locked_at | string |
is_closed | boolean |
Example response
{
"data": {
"id": "a8f1…",
"locked_at": "2026-05-12T14:00:00Z",
"is_closed": false
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/opening-balances
fiscal-periods.opening-balances · scope bookkeeping:write
Generate opening-balance verifikation for the next fiscal period.
Reads the closed period's trial balance, filters to BAS class 1-2 accounts with non-zero closing balance, and posts an opening verifikation (status=posted) onto the next_period_id. Sync. The path id is the CLOSED period; body.next_period_id is the target.
Use when: After /year-end + /close on a period, generate the IB into the next period so the new year starts with the correct balance sheet.
Don't use for: Posting opening balances on a manually-edited basis (use POST /journal-entries with source_type=manual). Re-running on the same target period (will produce duplicate IB entries).
Pitfalls
- Idempotency-Key is mandatory.
- next_period_id must reference the SAME company and must NOT already have an IB entry. The engine throws if it does.
- Only class 1 (assets) and 2 (equity/liabilities) flow into the IB; class 3-8 are zeroed by the closing entry.
Risk: high · Idempotent: yes · Reversible: yes · Dry-run supported: no
Request body
| Name | Type | Required |
|---|---|---|
next_period_id | string | yes |
Response fields
| Name | Type |
|---|---|
opening_entry_id | string |
voucher_series | string |
voucher_number | number |
next_period_id | string |
Example request
{
"next_period_id": "7b3a…"
}
Example response
{
"data": {
"opening_entry_id": "4d2a…",
"voucher_series": "A",
"voucher_number": 1,
"next_period_id": "7b3a…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/correct
opening-balances.correct · scope bookkeeping:write
Correct a year's ingående balanser by storno: the full corrected IB replaces the old one.
Books the corrected IB verifikat from the lines given (the complete IB, not a difference), stornoes the old IB verifikat and relinks the year to the new one (BFL 5 kap 5 §: nothing posted is edited). The new verifikat's text references the one it corrects. With cascade=true the per-account change is also carried into every later year's IB; years that are closed, locked, behind the lock date or have a bokslut are skipped and reported. Only for an open, unlocked year with an IB and no bokslut. Idempotent. Dry-runnable: the dry run previews the new verifikat and the per-account change and writes nothing.
Use when: The IB booked for a year was wrong (a typo, a balance the previous system corrected later).
Don't use for: A year without an IB (POST /fiscal-periods/{id}/opening-balances/manual), a single wrong line in an open year (POST /journal-entries/{id}/strike-lines on the IB verifikat corrects inside the same verifikat), or a year that is locked, closed or has a bokslut (unwind those first).
Pitfalls
- Send the COMPLETE corrected IB: accounts left out end at zero.
- A year without an IB answers 409 OB_CORRECT_NO_EXISTING; one with a posted bokslut 409 OB_CORRECT_YEAR_END_EXISTS.
- A company lock date on or after the year's start answers 409 OB_COMPANY_LOCK_DATE.
- If the storno or relink fails the new IB is stornoed again and 500 OB_CORRECT_FAILED names both entry ids: the year keeps its old IB.
- cascade is best effort per later year: read cascade.skipped in the response and correct those years by hand.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
lines | object[] | yes | The IB per account. Zero rows are dropped; debit must equal credit. |
cascade | boolean | no | Also carry the per-account change into every later year's IB. Default false. |
Response fields
| Name | Type | Description |
|---|---|---|
journal_entry_id | string | |
voucher_series | string | null | |
voucher_number | number | null | |
reversed_entry_id | string | The old IB verifikat, now stornoed. |
fiscal_period_id | string | |
lines_created | number | |
total_debit | number | |
total_credit | number | |
cascade | object (optional) |
Example request
{
"lines": [
{
"account_number": "1930",
"amount": 84250.5
},
{
"account_number": "1510",
"debit_amount": 12500,
"credit_amount": 0
},
{
"account_number": "2440",
"amount": -9800
},
{
"account_number": "2081",
"amount": -25000
},
{
"account_number": "2099",
"amount": -61950.5
}
],
"cascade": true
}
Example response
{
"data": {
"journal_entry_id": "8c1e…",
"voucher_series": "A",
"voucher_number": 42,
"reversed_entry_id": "4d2a…",
"fiscal_period_id": "7b3a…",
"lines_created": 5,
"total_debit": 96750.5,
"total_credit": 96750.5,
"cascade": {
"corrected": [],
"skipped": []
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/manual
opening-balances.set-manual · scope bookkeeping:write
Book a fiscal year's ingående balanser (IB) from explicit lines, for a company new to Accounted.
Posts the IB verifikat (source_type opening_balance, series A, dated the year's first day) through the bookkeeping engine and links it to the year, as the dashboard's opening balance import does. Lines are balance sheet accounts only (class 1-2), zero rows dropped, at least two left, debit equal to credit. BAS accounts missing from the chart are activated. Refused when the year already has an IB, is closed or locked, or starts on or before the company lock date. Idempotent. Dry-runnable: the dry run previews the verifikat and writes nothing.
Use when: The company moved from another system without an SIE file and its first year in Accounted needs the balances from the previous system's balansräkning.
Don't use for: Carrying the IB forward from a year closed in Accounted (POST /fiscal-periods/{id}/opening-balances, or the year-end which does it), an SIE migration (POST /imports/sie brings its own IB), or changing an IB already booked (POST /fiscal-periods/{id}/opening-balances/correct).
Pitfalls
- A year that already has an IB answers 409 OB_PERIOD_ALREADY_HAS_BALANCES with details.existingEntryId: correct it instead.
- Class 3-8 accounts answer 400 OB_PNL_ACCOUNT, class 0 and 9 400 OB_NON_BALANCE_SHEET_ACCOUNT: an IB holds balance sheet accounts only, earlier years' results sit in equity (20xx).
- Debit and credit must match to the öre: 400 OB_UNBALANCED with details.diff.
- A company lock date on or after the year's start answers 409 OB_SET_COMPANY_LOCK_DATE; a closed or locked year 400 OB_PERIOD_CLOSED or OB_PERIOD_LOCKED.
- The IB is a posted verifikat: it is never edited or deleted, only corrected by storno through /opening-balances/correct.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
lines | object[] | yes | The IB per account. Zero rows are dropped; debit must equal credit. |
Response fields
| Name | Type |
|---|---|
journal_entry_id | string |
voucher_series | string | null |
voucher_number | number | null |
fiscal_period_id | string |
entry_date | string |
lines_created | number |
total_debit | number |
total_credit | number |
Example request
{
"lines": [
{
"account_number": "1930",
"amount": 84250.5
},
{
"account_number": "1510",
"debit_amount": 12500,
"credit_amount": 0
},
{
"account_number": "2440",
"amount": -9800
},
{
"account_number": "2081",
"amount": -25000
},
{
"account_number": "2099",
"amount": -61950.5
}
]
}
Example response
{
"data": {
"journal_entry_id": "4d2a…",
"voucher_series": "A",
"voucher_number": 1,
"fiscal_period_id": "7b3a…",
"entry_date": "2026-01-01",
"lines_created": 5,
"total_debit": 96750.5,
"total_credit": 96750.5
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/opening-balances/split-per-project
opening-balances.split-per-project · scope bookkeeping:write
Split a year's ingående balanser per project, inside the same IB verifikat (inline rättelse).
Applies the split GET /fiscal-periods/{id}/opening-balances/split-per-project previews: for each changing account the IB verifikat's lines are struck and replaced by one line per project plus an untagged remainder, inside the same verifikat through the inline rättelse (BFL 5 kap 5 §; the struck lines are kept in the rättelse log with who and when). Account totals never change. Every project code is checked against the dimension registry first; archived projects are kept. Only for an open, unlocked year after the lock date and without a bokslut; there is no storno fallback, because a storno-corrected IB carries no project tags. A split already in place answers applied=false and writes nothing. Idempotent. Dry-runnable: the dry run answers the preview.
Use when: The user wants project balances carried into a year whose IB was booked as one line per account, after reviewing the preview.
Don't use for: Changing IB amounts (POST /fiscal-periods/{id}/opening-balances/correct), or a locked or closed year: open it first.
Pitfalls
- Pass expected_fingerprint from the preview: if the IB or the previous year changed since, 409 OB_SPLIT_PROPOSAL_CHANGED instead of a different split.
- A closed year answers 409 OB_SPLIT_PERIOD_CLOSED, a locked one 409 OB_SPLIT_PERIOD_LOCKED, a lock date covering the IB 409 OB_COMPANY_LOCK_DATE, a posted bokslut 409 OB_CORRECT_YEAR_END_EXISTS.
- Project codes missing from the registry answer 409 OB_SPLIT_DIMENSION_UNRESOLVED with details.unresolved.
- An account with a foreign-currency IB line or a line-level underlag link, or whose IB already carries a different project split, is skipped (accounts_skipped), never forced.
- With nothing to change the apply answers 200 applied=false, but a dry run (and MCP staging) answers 409 OB_SPLIT_NOTHING_TO_DO: there is nothing to approve.
- Over 100 new lines run as several inline rättelser. A failure after one committed answers the error with details.accounts_changed and details.rattelse_log_ids; the books stay consistent and a rerun continues from there.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
expected_fingerprint | string | no | fingerprint from the preview you reviewed. The split is refused if it changed since. |
Response fields
| Name | Type | Description |
|---|---|---|
fiscal_period_id | string | |
journal_entry_id | string | The IB verifikat, corrected in place (same id). |
applied | boolean | False when the IB already matched the split: nothing was written. |
accounts_changed | string[] | |
accounts_skipped | object[] | |
lines_struck | number | |
lines_added | number | |
rattelse_log_ids | string[] | |
fingerprint | string |
Example request
{
"expected_fingerprint": "3f9a…"
}
Example response
{
"data": {
"fiscal_period_id": "7b3a…",
"journal_entry_id": "4d2a…",
"applied": true,
"accounts_changed": [
"1470"
],
"accounts_skipped": [],
"lines_struck": 1,
"lines_added": 2,
"rattelse_log_ids": [
"b7d2…"
],
"fingerprint": "3f9a…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/reopen-external
fiscal-periods.reopen-external · scope bookkeeping:write
Undo klarmarkera: reopen a year marked closed in the previous system.
Reopens and unlocks a räkenskapsår that close-external closed, and writes the decision to the audit log. Only while that close is still the klarmarkera one: a year closed by a year-end run in Accounted is never reopened. Typical need: the prior-year SIE file was wrong and must be replaced. Idempotent. Dry-runnable.
Use when: A year was marked closed in the previous system by mistake, or its imported contents must be replaced.
Don't use for: Reopening a year closed by a year-end here (not possible), or unlocking a locked year (POST /fiscal-periods/{id}/unlock).
Pitfalls
- An open year answers 409 PERIOD_REOPEN_NOT_CLOSED.
- A year closed by a year-end run here answers 409 PERIOD_REOPEN_NOT_EXTERNAL.
- The lock is cleared too: lock or klarmarkera the year again once the correction is done.
Risk: high · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
id | string |
name | string |
period_start | string |
period_end | string |
is_closed | boolean |
closed_at | string | null |
closed_externally | boolean |
locked_at | string | null |
previous_period_id | string | null |
created_at | string |
Example response
{
"data": {
"id": "a8f1…",
"name": "Räkenskapsår 2024",
"period_start": "2024-01-01",
"period_end": "2024-12-31",
"is_closed": false,
"closed_at": null,
"closed_externally": false,
"locked_at": null,
"previous_period_id": "5c2e…",
"created_at": "2026-12-01T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/unlock
fiscal-periods.unlock · scope bookkeeping:write
Unlock a locked (not closed) fiscal year.
Clears locked_at so the year accepts postings again, and writes the unlock to the audit log (BFNAR 2013:2 behandlingshistorik). A closed year is never unlocked: past a close, corrections go into an open year as storno. Re-lock with POST /fiscal-periods/{id}/lock after the correction. Idempotent. Dry-runnable.
Use when: The user asked to correct something in a locked year, or a year-end must run on a year that was locked beforehand.
Don't use for: Getting a booking past a lock without the user asking for that correction, or reopening a closed year (a year marked closed in a previous system: POST /fiscal-periods/{id}/reopen-external).
Pitfalls
- A closed year answers 409 PERIOD_UNLOCK_CLOSED; an unlocked one 409 PERIOD_UNLOCK_NOT_LOCKED.
- The company-wide lock date (bookkeeping_locked_through) is a separate lock this does not touch.
Risk: high · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
id | string |
name | string |
period_start | string |
period_end | string |
is_closed | boolean |
closed_at | string | null |
closed_externally | boolean |
locked_at | string | null |
previous_period_id | string | null |
created_at | string |
Example response
{
"data": {
"id": "a8f1…",
"name": "Räkenskapsår 2027",
"period_start": "2027-01-01",
"period_end": "2027-12-31",
"is_closed": false,
"closed_at": null,
"closed_externally": false,
"locked_at": null,
"previous_period_id": "5c2e…",
"created_at": "2026-12-01T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/fiscal-periods/:id/year-end
fiscal-periods.year-end · scope bookkeeping:write
Execute year-end closing (currency revaluation + closing entry).
Async-operation endpoint. Runs the year-end closing flow: currency revaluation (FX gains/losses to 3960/7960), then posts the closing entry that zeroes class 3-8 onto årets resultat (2099 for AB, the relevant eget-kapital account in the 2010-2019 range for enskild firma: the engine resolves which based on company.entity_type). Returns 202 with operation_id; subscribe to operation.completed or poll /v1/operations/{id}.
Use when: After /lock and a passing /compliance/check?type=year_end_readiness, you want to run the closing entry. This is step 2 of the lock → year-end → close flow.
Don't use for: Re-running year-end (per-period idempotent: fails if closing_entry_id is already set). Closing the period (use /close after year-end succeeds).
Pitfalls
- Idempotency-Key is mandatory.
- Period must pass year_end_readiness checks (no drafts, no unexplained voucher gaps, trial balance balanced). The engine re-validates and aborts if not.
- Closing entry is itself a verifikation (posted): the period must NOT already be closed.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type |
|---|---|
operation_id | string |
type | "fiscal_periods.year_end" |
status | "queued" | "running" | "succeeded" | "failed" |
poll_url | string |
webhook_event | "operation.completed" |
Example response
{
"data": {
"operation_id": "0e9c…",
"type": "fiscal_periods.year_end",
"status": "succeeded",
"poll_url": "/api/v1/operations/0e9c…",
"webhook_event": "operation.completed"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
PATCH /api/v1/companies/:companyId/fiscal-periods/:id
fiscal-periods.update · scope bookkeeping:write
Rename or re-date an open fiscal year.
Sparse update of an open, unlocked räkenskapsår: name, period_start, period_end. The name can change at any time on an open year. The dates can move also when the year already holds verifikat, as long as nothing it holds is affected: every verifikat and draft stays inside the new dates, posted opening balances keep the start, and no lock, year-end work, filing or SIE hold covers the change. New dates follow the same BFL 3 kap. rules as create (18-month cap, month-end end, 1st-of-month start unless it is the earliest year, calendar year for an enskild firma), may not overlap another year and keep the chain to the neighbouring years. Idempotent. Dry-runnable.
Use when: A year was created with the wrong dates or name, for example a first year that starts a day after the registration date at Bolagsverket.
Don't use for: Moving verifikat between years, changing a locked or closed year, or re-dating a year whose annual report or declaration is already filed.
Pitfalls
- A re-date that would affect what the year holds answers 409 FISCAL_PERIOD_DATES_BLOCKED; details.blockers names every reason (for example entries_outside with the first and last date outside the new dates). Send only name to rename.
- A locked year answers 409 FISCAL_PERIOD_UPDATE_LOCKED, a closed one 409 FISCAL_PERIOD_UPDATE_CLOSED.
- Only the earliest year can move its start and only the latest year its end: a boundary shared with a neighbouring year answers 400 FISCAL_PERIOD_NOT_CONTIGUOUS.
Risk: medium · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required |
|---|---|---|
name | string | no |
period_start | string | no |
period_end | string | no |
Response fields
| Name | Type |
|---|---|
id | string |
name | string |
period_start | string |
period_end | string |
is_closed | boolean |
closed_at | string | null |
closed_externally | boolean |
locked_at | string | null |
previous_period_id | string | null |
created_at | string |
Example request
{
"period_end": "2027-06-30"
}
Example response
{
"data": {
"id": "a8f1…",
"name": "Räkenskapsår 2027",
"period_start": "2027-01-01",
"period_end": "2027-06-30",
"is_closed": false,
"closed_at": null,
"closed_externally": false,
"locked_at": null,
"previous_period_id": "5c2e…",
"created_at": "2026-12-01T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
PATCH /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/compliance
arsredovisning.update-compliance · scope bookkeeping:write
Answer the årsredovisning compliance questions and record the confirmations.
Sparse update of the compliance profile the ÅRL, K2 and K3 checks read: publikt bolag, likvidation, listed securities, moderföretag and group size, foreign branch, crypto, share-based payments, convertibles, building revenue share, deferred tax, reporting currency, revisionsberättelse, dividend prudence. narrative_confirmed, k2_assessment_confirmed and signer_roster_confirmed record (true) or withdraw (false) the user's confirmations with a timestamp. Answers the recomputed eligibility, validation and capabilities. Idempotent. Dry-runnable.
Use when: The validation (gnubok_validate_arsredovisning) reports an unanswered compliance question or a missing confirmation.
Don't use for: The document texts (POST .../arsredovisning/narrative) or the signer roster itself (POST .../arsredovisning/signatures).
Pitfalls
- An unanswered question is null, never false: false is a legal assertion that the condition does not apply.
- signer_roster_confirmed asserts the roster matches the board and VD registered at Bolagsverket; any later roster change clears it.
- is_parent_company false also clears parent_group_size and prepares_consolidated_accounts.
- Send at least one field (400 VALIDATION_ERROR otherwise).
Risk: medium · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
is_public_limited_company | boolean | null | no | |
is_in_liquidation | boolean | null | no | |
securities_traded_on_regulated_market | boolean | null | no | |
is_parent_company | boolean | null | no | |
parent_group_size | "none" | "small" | "large" | null | no | |
prepares_consolidated_accounts | boolean | null | no | |
has_foreign_branch | boolean | null | no | |
has_crypto_assets | boolean | null | no | |
has_share_based_payments | boolean | null | no | |
has_convertible_debt | boolean | null | no | |
building_revenue_share_pct | number | null | no | |
has_material_deferred_tax | boolean | null | no | |
reporting_currency | "SEK" | "EUR" | no | |
auditor_report_required | boolean | null | no | |
auditor_report_included | boolean | no | |
dividend_prudence_confirmed | boolean | null | no | |
narrative_confirmed | boolean | no | The user has reviewed the förvaltningsberättelse texts. |
k2_assessment_confirmed | boolean | no | The user confirms the K2 eligibility assessment. |
signer_roster_confirmed | boolean | no | The user confirms the signer roster matches the board and VD registered at Bolagsverket. |
Response fields
| Name | Type | Description |
|---|---|---|
profile | object | The compliance profile answers and confirmation timestamps (annual_report_profile_id, not id). |
disclosures | object | |
eligibility | object | K2 eligibility, size classification and digital filing eligibility. |
validation | object | Draft-stage validation: ok, error_count, warning_count, issues. |
capabilities | object | |
report_summary | object |
Example request
{
"is_public_limited_company": false,
"k2_assessment_confirmed": true
}
Example response
{
"data": {
"profile": {
"annual_report_profile_id": "9d3a…",
"is_public_limited_company": false
},
"validation": {
"stage": "draft",
"ok": true,
"error_count": 0,
"warning_count": 1,
"issues": []
},
"report_summary": {
"proposed_dividend": 0,
"distributable_equity": 412000
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
PATCH /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures/:signatureId
arsredovisning.record-signature · scope bookkeeping:write
Record that a signer signed the frozen årsredovisning version, or declined.
status signed records the evidence of a signature made outside the product (a paper original or an advanced e-signature): the ready_for_signature version it is for, the method, where the original is kept (evidence_reference) and the signing time. status declined marks the slot declined. Only a pending slot of this period transitions, once. ÅRL requires each signer to date their signature; the date must fall between the version's finalization and today. Idempotent. Dry-runnable.
Use when: The board has signed the printed or e-signed document and the signatures are to be registered.
Don't use for: Adding signers (POST .../signatures), filing with Bolagsverket, or BankID signing (not available here).
Pitfalls
- A version that is not ready_for_signature answers 409 ARSREDOVISNING_VERSION_NOT_SIGNABLE.
- A slot already signed or declined, bound to another version, or of another period answers 409 SIGNATURE_INVALID_TRANSITION.
- A signed_at before finalization or in the future answers 400 ARSREDOVISNING_SIGNATURE_DATE_INVALID.
- evidence_reference is archive:<ref>, document:<id> or receipt:<ref>, not free text.
- Recorded signatures cannot be undone: finalize a new version instead.
Risk: medium · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
status | "signed" | "declined" | yes | |
annual_report_version_id | string | no | Required for signed: the ready_for_signature version. |
signing_method | "paper_original" | "advanced_e_signature" | "bankid" | no | Required for signed. |
evidence_reference | string | no | Required for signed: archive:<ref>, document:<id> or receipt:<ref>. |
signed_at | string | no | signed only: ISO timestamp, defaults to now. |
Response fields
| Name | Type | Description |
|---|---|---|
signature_id | string | |
fiscal_period_id | string | |
annual_report_version_id | string | null | Null while the slot is on the unbound roster. |
role | string | |
signer_name | string | |
status | "pending" | "signed" | "declined" | |
signed_at | string | null | |
signing_method | "paper_original" | "advanced_e_signature" | "bankid" | "bolagsverket" | null | |
evidence_reference | string | null | |
evidence_recorded_at | string | null | |
created_at | string |
Example request
{
"status": "signed",
"annual_report_version_id": "4e7d…",
"signing_method": "paper_original",
"evidence_reference": "archive:AR-2026-1",
"signed_at": "2027-03-05T10:00:00Z"
}
Example response
{
"data": {
"signature_id": "5b1c…",
"fiscal_period_id": "7c2b…",
"annual_report_version_id": "4e7d…",
"role": "Styrelseledamot",
"signer_name": "Anna Andersson",
"status": "signed",
"signed_at": "2027-03-05T10:00:00Z",
"signing_method": "paper_original",
"evidence_reference": "archive:AR-2026-1",
"evidence_recorded_at": null,
"created_at": "2027-03-02T09:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
DELETE /api/v1/companies/:companyId/fiscal-periods/:id/arsredovisning/signatures/:signatureId
arsredovisning.remove-signatory · scope bookkeeping:write
Remove a signer from the årsredovisning roster before a version binds it.
Deletes a pending signer slot that no version has bound yet. Slots bound to a finalized version stay (they are part of what gets signed): finalize a new version with the corrected roster instead. Any roster change clears the signer roster confirmation. Idempotent. Dry-runnable.
Use when: A signer was added by mistake or has left the board before the version is finalized.
Don't use for: Declining a bound slot (PATCH .../signatures/{signatureId} {"status": "declined"}).
Pitfalls
- A bound, signed or declined slot answers 409 ARSREDOVISNING_SIGNER_ROSTER_LOCKED.
- Takes no body.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
signature_id | string |
deleted | true |
Example response
{
"data": {
"signature_id": "5b1c…",
"deleted": true
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}