Menu

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

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

NameTypeRequiredDescription
status"registered" | "paid"noregistered = still owed, paid = repaid.
employee_idstringnoOnly this employee's claims.
cursorstringnonext_cursor from the previous page. Omit for the first page.
limitnumbernoPage size, 1-100 (default 50).

Response fields

NameType
expense_claimsobject[]
next_cursorstring | 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

NameTypeDescription
expense_claim_idstring
employee_idstring | nullThe employee owed, or null for the owner (or a förening member).
claimant_namestring
descriptionstring
expense_datestring
amount_seknumberGross amount incl. VAT in SEK: what the company owes the person.
vat_seknumberDeductible ingående moms booked on 2641, in SEK.
currencystringThe receipt currency; amounts are booked in SEK.
amount_in_currencynumber | null
exchange_ratenumber | null
expense_accountstringBAS cost account (class 4-8), as a string.
liability_accountstringWho is owed: 2893 AB owner, 2018 EF owner (egen insättning), 2890 förening member, 2820 employee.
document_idstring | null
status"registered" | "paid"
journal_entry_idstring | nullThe verifikat that booked the claim.
payout_batch_idstring | nullThe payout that repaid it, once paid.
created_atstring

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

NameTypeRequiredDescription
dry_runstringnotrue (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits.

Request body

NameTypeRequired
descriptionstringyes
expense_datestringyes
amountnumberyes
vat_amountnumberno
currency"SEK" | "EUR" | "USD" | "GBP" | "NOK" | "DKK" | "CHF"no
exchange_ratenumberno
expense_accountstringyes
employee_idstring | nullno
claimant_namestringno
document_idstring | nullno
inbox_item_idstring | nullno
linesobject[]no

Response fields

NameTypeDescription
expense_claim_idstring
employee_idstring | nullThe employee owed, or null for the owner (or a förening member).
claimant_namestring
descriptionstring
expense_datestring
amount_seknumberGross amount incl. VAT in SEK: what the company owes the person.
vat_seknumberDeductible ingående moms booked on 2641, in SEK.
currencystringThe receipt currency; amounts are booked in SEK.
amount_in_currencynumber | null
exchange_ratenumber | null
expense_accountstringBAS cost account (class 4-8), as a string.
liability_accountstringWho is owed: 2893 AB owner, 2018 EF owner (egen insättning), 2890 förening member, 2820 employee.
document_idstring | null
status"registered" | "paid"
journal_entry_idstring | nullThe verifikat that booked the claim.
payout_batch_idstring | nullThe payout that repaid it, once paid.
created_atstring

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

NameTypeRequiredDescription
dry_runstringnotrue (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits.

Request body

NameTypeRequired
claim_idsstring[]yes
payout_datestringyes
cash_accountstringyes
notesstringno

Response fields

NameType
batch_idstring
journal_entry_idstring
voucher_numbernumber | null
total_seknumber
claim_countnumber

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

NameTypeRequiredDescription
dry_runstringnotrue (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits.

Response fields

NameType
salary_run_idstring
employee_idstring
claim_countnumber
total_seknumber
linesobject[]

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

NameTypeRequiredDescription
dry_runstringnotrue (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits.

Request body

NameTypeRequired
claim_idsstring[]yes

Response fields

NameType
batch_idstring
journal_entry_idstring
voucher_numbernumber | null
total_seknumber
claim_countnumber
transaction_idstring

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

NameTypeRequiredDescription
dry_runstringnotrue (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits.

Response fields

NameTypeDescription
deletedtrue
expense_claim_idstring
reversal_entry_idstring | nullThe 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"
  }
}