Menu
Cookbooks
API reference
- Overview
- Companies
- 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
Expense claims
Utlägg: costs an owner or employee paid privately. Register, list, delete, record the payout, and match a bank payout to open claims.
Endpoints
GET/api/v1/companies/:companyId/expense-claims: List expense claims (utlägg): what the company owes owners and employees for private purchases.GET/api/v1/companies/:companyId/expense-claims/:id: Read one expense claim (utlägg).POST/api/v1/companies/:companyId/expense-claims: Register an expense claim (utlägg) and post its verifikat.POST/api/v1/companies/:companyId/expense-claims/payouts: Record that the company paid a person back for their expense claims.POST/api/v1/companies/:companyId/salary-runs/:id/employees/:employeeId/expense-claims: Repay an employee's open expense claims (utlägg) with this salary run.POST/api/v1/companies/:companyId/transactions/:id/match-expense-payout: Book an outgoing bank transaction as the repayment of one person's expense claims.DELETE/api/v1/companies/:companyId/expense-claims/:id: Delete a registered expense claim; its verifikat is reversed by storno, never deleted.
GET /api/v1/companies/:companyId/expense-claims
expense-claims.list · scope suppliers:read
List expense claims (utlägg): what the company owes owners and employees for private purchases.
Returns the utlägg register newest first: each claim's claimant, SEK amount, VAT, cost and liability account, status (registered = still owed, paid = repaid) and the verifikat that booked it. Filter by status or employee_id. Cursor pagination: pass next_cursor back as cursor; next_cursor is null on the last page.
Use when: You need the open claims before paying someone back or matching a bank transfer, or you are reconciling 2893/2820/2890 against who is owed what.
Don't use for: Supplier invoices (GET /supplier-invoices) or salary (the payroll endpoints); an utlägg put on a payslip is still listed here as registered until the salary run is booked.
Pitfalls
- amount_sek is gross incl. VAT: the amount owed, not the cost.
- A claim on 2018 (enskild firma owner) is an egen insättning, not a debt; it is listed but is not normally paid back.
- The page is in data.expense_claims with data.next_cursor; a cursor that no longer decodes starts from the first page.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | "registered" | "paid" | no | registered = still owed, paid = repaid. |
employee_id | string | no | Only this employee's claims. |
cursor | string | no | next_cursor from the previous page. Omit for the first page. |
limit | number | no | Page size, 1-100 (default 50). |
Response fields
| Name | Type |
|---|---|
expense_claims | object[] |
next_cursor | string | null |
Example response
{
"data": {
"expense_claims": [
{
"expense_claim_id": "5a0a…",
"employee_id": null,
"claimant_name": "Anna Svensson",
"description": "USB-hubb",
"expense_date": "2026-09-01",
"amount_sek": 500,
"vat_sek": 100,
"currency": "SEK",
"amount_in_currency": null,
"exchange_rate": null,
"expense_account": "5410",
"liability_account": "2893",
"document_id": null,
"status": "registered",
"journal_entry_id": "9c1e…",
"payout_batch_id": null,
"created_at": "2026-09-01T09:12:00Z"
}
],
"next_cursor": null
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/expense-claims/:id
expense-claims.get · scope suppliers:read
Read one expense claim (utlägg).
Returns one claim: who is owed, the SEK amount and VAT, the cost and liability accounts, its status and the verifikat that booked it (journal_entry_id) and, once paid, the payout batch.
Use when: You hold an expense_claim_id (from the list or a create) and need its current status.
Don't use for: Finding claims: list them with GET /expense-claims?status=registered.
Pitfalls
- An id from another company answers 404 EXPENSE_CLAIM_NOT_FOUND.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
expense_claim_id | string | |
employee_id | string | null | The employee owed, or null for the owner (or a förening member). |
claimant_name | string | |
description | string | |
expense_date | string | |
amount_sek | number | Gross amount incl. VAT in SEK: what the company owes the person. |
vat_sek | number | Deductible ingående moms booked on 2641, in SEK. |
currency | string | The receipt currency; amounts are booked in SEK. |
amount_in_currency | number | null | |
exchange_rate | number | null | |
expense_account | string | BAS cost account (class 4-8), as a string. |
liability_account | string | Who is owed: 2893 AB owner, 2018 EF owner (egen insättning), 2890 förening member, 2820 employee. |
document_id | string | null | |
status | "registered" | "paid" | |
journal_entry_id | string | null | The verifikat that booked the claim. |
payout_batch_id | string | null | The payout that repaid it, once paid. |
created_at | string |
Example response
{
"data": {
"expense_claim_id": "5a0a…",
"employee_id": null,
"claimant_name": "Anna Svensson",
"description": "USB-hubb",
"expense_date": "2026-09-01",
"amount_sek": 500,
"vat_sek": 100,
"currency": "SEK",
"amount_in_currency": null,
"exchange_rate": null,
"expense_account": "5410",
"liability_account": "2893",
"document_id": null,
"status": "registered",
"journal_entry_id": "9c1e…",
"payout_batch_id": null,
"created_at": "2026-09-01T09:12:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/expense-claims
expense-claims.create · scope suppliers:write
Register an expense claim (utlägg) and post its verifikat.
Books a business cost someone paid privately: Debit the cost account (net), Debit 2641 (vat_amount), Credit the person's liability account (gross), in one verifikat posted immediately. The liability account follows the claimant: employee_id books 2820; otherwise the owner's account for the legal form (2893 aktiebolag, 2018 enskild firma as egen insättning, 2890 förening member) with claimant_name. Foreign currency converts at exchange_rate or Riksbanken's rate for expense_date. lines replaces the generated rows (reverse charge, templates) and must credit the liability account with exactly amount. document_id attaches the receipt to the verifikat; inbox_item_id marks the inbox item booked. Idempotent. Dry-runnable.
Use when: A receipt was paid with a private card or cash: the answer to "Vem betalade?" is the owner or an employee, not the company account.
Don't use for: A purchase the company paid itself (categorize the bank transaction or register a supplier invoice), an unpaid supplier invoice (POST /supplier-invoices) or mileage (körjournal).
Pitfalls
- amount is gross incl. VAT and vat_amount must be below it; foreign VAT is not deductible on 2641, so send vat_amount 0 for a foreign receipt.
- The verifikat is posted at once and is immutable: undo it with DELETE /expense-claims/{id}, which posts a storno.
- employee_id wins over claimant_name: the employee's own name is stored.
- A date in a locked period or behind the company lock date returns 400 PERIOD_LOCKED; no open fiscal year returns EXPENSE_CLAIM_NO_FISCAL_PERIOD.
- expense_account is a STRING in class 4-8 ("5410"), never a number.
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 | string | yes |
expense_date | string | yes |
amount | number | yes |
vat_amount | number | no |
currency | "SEK" | "EUR" | "USD" | "GBP" | "NOK" | "DKK" | "CHF" | no |
exchange_rate | number | no |
expense_account | string | yes |
employee_id | string | null | no |
claimant_name | string | no |
document_id | string | null | no |
inbox_item_id | string | null | no |
lines | object[] | no |
Response fields
| Name | Type | Description |
|---|---|---|
expense_claim_id | string | |
employee_id | string | null | The employee owed, or null for the owner (or a förening member). |
claimant_name | string | |
description | string | |
expense_date | string | |
amount_sek | number | Gross amount incl. VAT in SEK: what the company owes the person. |
vat_sek | number | Deductible ingående moms booked on 2641, in SEK. |
currency | string | The receipt currency; amounts are booked in SEK. |
amount_in_currency | number | null | |
exchange_rate | number | null | |
expense_account | string | BAS cost account (class 4-8), as a string. |
liability_account | string | Who is owed: 2893 AB owner, 2018 EF owner (egen insättning), 2890 förening member, 2820 employee. |
document_id | string | null | |
status | "registered" | "paid" | |
journal_entry_id | string | null | The verifikat that booked the claim. |
payout_batch_id | string | null | The payout that repaid it, once paid. |
created_at | string |
Example request
{
"description": "USB-hubb",
"expense_date": "2026-09-01",
"amount": 500,
"vat_amount": 100,
"expense_account": "5410",
"claimant_name": "Anna Svensson"
}
Example response
{
"data": {
"expense_claim_id": "5a0a…",
"employee_id": null,
"claimant_name": "Anna Svensson",
"description": "USB-hubb",
"expense_date": "2026-09-01",
"amount_sek": 500,
"vat_sek": 100,
"currency": "SEK",
"amount_in_currency": null,
"exchange_rate": null,
"expense_account": "5410",
"liability_account": "2893",
"document_id": null,
"status": "registered",
"journal_entry_id": "9c1e…",
"payout_batch_id": null,
"created_at": "2026-09-01T09:12:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/expense-claims/payouts
expense-claims.record-payout · scope suppliers:write
Record that the company paid a person back for their expense claims.
Books one repayment of N registered claims of ONE person: Debit the liability account (2013 eget uttag for an enskild firma owner's 2018), Credit cash_account (19xx), for the claims' total, and marks them paid, all in one transaction that locks the claims. No money moves: this records a transfer made outside Accounted. Idempotent. Dry-runnable.
Use when: The person was paid back from an account without a bank feed, or the transfer cannot be matched to a bank row.
Don't use for: A repayment that is a bank transaction in Accounted: match it (POST /transactions/{id}/match-expense-payout), or the row gets booked twice. Repayment through salary is the payroll flow.
Pitfalls
- All claims must belong to one person and one liability account (400 EXPENSE_PAYOUT_MIXED_CLAIMANTS / MIXED_LIABILITY).
- A paid claim returns 409 EXPENSE_PAYOUT_ALREADY_PAID; one on a payslip returns 409 EXPENSE_PAYOUT_ON_PAYSLIP.
- cash_account is a STRING 19xx ("1930") that must be active in the chart.
- Partial payouts are not supported: pay whole claims.
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 |
|---|---|---|
claim_ids | string[] | yes |
payout_date | string | yes |
cash_account | string | yes |
notes | string | no |
Response fields
| Name | Type |
|---|---|
batch_id | string |
journal_entry_id | string |
voucher_number | number | null |
total_sek | number |
claim_count | number |
Example request
{
"claim_ids": [
"5a0a…"
],
"payout_date": "2026-09-05",
"cash_account": "1930"
}
Example response
{
"data": {
"batch_id": "e1f0…",
"journal_entry_id": "4d2a…",
"voucher_number": 118,
"total_sek": 500,
"claim_count": 1
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/salary-runs/:id/employees/:employeeId/expense-claims
salary-runs.attach-expense-claims · scope payroll:write
Repay an employee's open expense claims (utlägg) with this salary run.
Adds one tax-free expense_reimbursement line to the employee's payslip on a draft run for every registered expense claim of theirs that is not already on a payslip. The amount and liability account are copied from each claim (the server resolves them; nothing about amounts is sent). The lines raise the net payout only: no tax, no arbetsgivaravgifter, outside the AGI. Booking the run debits the liability account and marks exactly these claims paid. Recalculate the run afterwards. Dry-runnable: the dry run lists the claims that would be added.
Use when: The employee paid a business expense privately, the claim is registered, and it should be repaid with the salary instead of a separate bank transfer.
Don't use for: Registering the expense claim itself (the expense-claims flow books it), taxable allowances (add a payslip line), or repaying by bank transfer.
Pitfalls
- Draft runs only: a run past draft returns 400 SALARY_RUN_LINE_NOT_DRAFT (revert it first).
- The employee must be on the run: otherwise 404 SALARY_RUN_EMPLOYEE_NOT_FOUND.
- No open claims returns 404 SALARY_RUN_NO_OPEN_EXPENSE_CLAIMS; a claim that lands on another payslip concurrently returns 409 EXPENSE_CLAIM_ALREADY_ON_PAYSLIP.
- Adds all open claims of the employee at once; remove a line you do not want with DELETE /salary-runs/{id}/lines/{lineId}.
- Run POST /salary-runs/{id}/calculate afterwards so the totals include the lines.
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. |
Response fields
| Name | Type |
|---|---|
salary_run_id | string |
employee_id | string |
claim_count | number |
total_sek | number |
lines | object[] |
Example response
{
"data": {
"salary_run_id": "run_a8f1…",
"employee_id": "emp_1…",
"claim_count": 1,
"total_sek": 450,
"lines": [
{
"salary_line_id": "line_1…",
"expense_claim_id": "claim_1…",
"description": "Utlägg: Tågbiljett (2026-09-03)",
"amount": 450,
"account_number": "2820"
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/transactions/:id/match-expense-payout
transactions.match-expense-payout · scope transactions:write
Book an outgoing bank transaction as the repayment of one person's expense claims.
Books the bank row as the payout of the given registered claims: Debit their liability account (2013 for an enskild firma owner's 2018), Credit the transaction's own cash account, dated the transaction date, and links the row to the verifikat in the same transaction, so it can never be booked twice. The claims' total must equal the outflow to the öre. Idempotent. Dry-runnable.
Use when: An unbooked SEK outflow is the transfer that paid an owner or employee back for their utlägg.
Don't use for: A transfer with no bank row in Accounted (POST /expense-claims/payouts), partial repayments, or salary.
Pitfalls
- The sum of the picked claims must equal |amount| exactly: 400 EXPENSE_PAYOUT_MATCH_AMOUNT otherwise.
- Only unbooked outgoing SEK rows: incoming returns EXPENSE_PAYOUT_MATCH_NOT_EXPENSE, another currency EXPENSE_PAYOUT_MATCH_CURRENCY, an already booked row EXPENSE_PAYOUT_MATCH_TX_ALREADY_LINKED.
- All claims must belong to one person and one liability account.
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 |
|---|---|---|
claim_ids | string[] | yes |
Response fields
| Name | Type |
|---|---|
batch_id | string |
journal_entry_id | string |
voucher_number | number | null |
total_sek | number |
claim_count | number |
transaction_id | string |
Example request
{
"claim_ids": [
"5a0a…",
"7b1c…"
]
}
Example response
{
"data": {
"transaction_id": "1f2e…",
"batch_id": "e1f0…",
"journal_entry_id": "4d2a…",
"voucher_number": 119,
"total_sek": 1596,
"claim_count": 2
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
DELETE /api/v1/companies/:companyId/expense-claims/:id
expense-claims.delete · scope suppliers:write
Delete a registered expense claim; its verifikat is reversed by storno, never deleted.
Removes an unpaid claim from the register. The verifikat that booked it stays (BFL 5 kap 5 §): a storno verifikat reverses it and the receipt stays on the original. A claim scheduled on a draft payslip has that line removed first. Paid claims and claims on a payslip past draft are refused. Answers reversal_entry_id (null when the original verifikat no longer exists). Idempotent. Dry-runnable.
Use when: A claim was registered by mistake (wrong person, duplicate receipt) and has not been paid back.
Don't use for: Correcting an amount or account on a booked claim (delete and register again, or correct the verifikat), or undoing a payout.
Pitfalls
- A paid claim returns 409 EXPENSE_CLAIM_ALREADY_PAID; a claim on a payslip past draft returns 409 EXPENSE_CLAIM_ON_PAYSLIP.
- The storno is a new verifikat with its own number: the original number is never freed.
- A locked period for the storno date surfaces as PERIOD_LOCKED from the engine.
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. |
Response fields
| Name | Type | Description |
|---|---|---|
deleted | true | |
expense_claim_id | string | |
reversal_entry_id | string | null | The storno verifikat, or null when no verifikat remained. |
Example response
{
"data": {
"deleted": true,
"expense_claim_id": "5a0a…",
"reversal_entry_id": "b7d2…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}