Menu

Supplier invoices

AP lifecycle: register, approve, book (deferred booking), mark paid, credit. With ROT/RUT and reverse-charge support.

Endpoints


GET /api/v1/companies/:companyId/supplier-invoices

supplier-invoices.list · scope suppliers:read

List supplier invoices for a company.

Cursor-paginated supplier-invoice list ordered by created_at DESC, id ASC (newest-registered first; the invoice_date column is the seller's invoice date and is filterable via ?date_from / ?date_to but is not the sort key). Filters: status, supplier_id, currency, date_from / date_to (filter by invoice_date).

Use when: You need to enumerate registered supplier invoices for an AP dashboard, a payment run, or a leverantörsreskontra reconciliation.

Don't use for: Fetching a single supplier invoice: use GET /supplier-invoices/{id}. Listing customer invoices (different resource).

Pitfalls

  • Credit notes (is_credit_note=true) appear in the same list as the originals; filter by status=credited or check the flag to separate.
  • remaining_amount is the unpaid portion; a partially_paid SI has remaining_amount > 0.
  • arrival_number is internal book-keeping, not the seller's invoice number: use supplier_invoice_number for matching to received documents.
  • Ordering is by created_at (registration time), not invoice_date. A late-registered invoice appears where it was registered: filter on ?date_from / ?date_to when you care about the seller's invoice date.
  • Cursor pagination: pass ?cursor=<next_cursor> from the previous response. A stale or tampered cursor is ignored and the first page is returned again.

Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no

Query parameters

NameTypeRequiredDescription
status"registered" | "approved" | "paid" | "partially_paid" | "overdue" | "disputed" | "credited" | "reversed"noOnly supplier invoices in this status.
supplier_idstringnoOnly invoices from this supplier (id).
currencystringno3-letter ISO 4217 code, uppercase (e.g. SEK, EUR).
date_fromstringnoYYYY-MM-DD. Invoices with invoice_date on or after this date.
date_tostringnoYYYY-MM-DD. Invoices with invoice_date on or before this date.
cursorstringnoOpaque cursor from the previous page's meta.next_cursor. Omit for the first page.
limitnumbernoPage size, 1-100 (default 50). Larger values are clamped to 100.

Response fields

NameType
[].idstring
[].supplier_idstring
[].supplier_namestring
[].arrival_numbernumber
[].supplier_invoice_numberstring
[].invoice_datestring
[].due_datestring
[].status"registered" | "approved" | "paid" | "partially_paid" | "overdue" | "disputed" | "credited" | "reversed"
[].currencystring
[].subtotalnumber
[].vat_amountnumber
[].totalnumber
[].paid_amountnumber
[].remaining_amountnumber
[].is_credit_noteboolean
[].paid_atstring | null
[].created_atstring

Example response

{
  "data": [
    {
      "id": "0e9c…",
      "supplier_id": "a8f1…",
      "supplier_name": "Office Depot AB",
      "arrival_number": 42,
      "supplier_invoice_number": "2026-1234",
      "invoice_date": "2026-05-10",
      "due_date": "2026-06-09",
      "status": "registered",
      "currency": "SEK",
      "subtotal": 1000,
      "vat_amount": 250,
      "total": 1250,
      "paid_amount": 0,
      "remaining_amount": 1250,
      "is_credit_note": false,
      "paid_at": null,
      "created_at": "2026-05-13T15:00:00Z"
    }
  ],
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12",
    "next_cursor": null
  }
}

GET /api/v1/companies/:companyId/supplier-invoices/:id

supplier-invoices.get · scope suppliers:read

Retrieve a single supplier invoice by id.

Returns the full supplier-invoice record. Pass ?expand=supplier,items,payments to embed the related rows in the same response.

Use when: You need the full record before approving, paying, or crediting it, or for audit trail / reconciliation.

Don't use for: Listing supplier invoices (use the list endpoint). Customer-invoice lookups (different resource).

Pitfalls

  • Credit notes return is_credit_note=true and a credited_invoice_id pointing at the original.
  • registration_journal_entry_id and payment_journal_entry_id let you trace the SI to its bokföring rows; they are null when no JE has been posted (e.g. on a kontantmetoden SI before payment).

Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no

Query parameters

NameTypeRequiredDescription
expandstringnoComma-separated related records to embed: supplier, items, payments. An unknown key returns 400 VALIDATION_ERROR.

Response fields

NameType
idstring
supplier_idstring
arrival_numbernumber
supplier_invoice_numberstring
invoice_datestring
due_datestring
received_datestring
delivery_datestring | null
statusstring
currencystring
exchange_ratenumber | null
subtotalnumber
vat_amountnumber
totalnumber
vat_treatmentstring
reverse_chargeboolean
paid_amountnumber
remaining_amountnumber
is_credit_noteboolean
credited_invoice_idstring | null
registration_journal_entry_idstring | null
payment_journal_entry_idstring | null
notesstring | null
created_atstring
updated_atstring

Example response

{
  "data": {
    "id": "0e9c…",
    "supplier_id": "a8f1…",
    "arrival_number": 42,
    "supplier_invoice_number": "2026-1234",
    "status": "registered",
    "currency": "SEK",
    "subtotal": 1000,
    "vat_amount": 250,
    "total": 1250,
    "remaining_amount": 1250,
    "is_credit_note": false
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices

supplier-invoices.create · scope suppliers:write

Register a new supplier invoice.

Creates a supplier invoice in registered status and posts the registration journal entry under faktureringsmetoden (Debit expense + Debit 2641 Ingående moms / Credit 2440 Leverantörsskulder). Under kontantmetoden no JE is posted at this stage. Under defer_invoice_booking (faktureringsmetoden, Registrera men bokför inte) no JE is posted either: book it afterwards with POST /supplier-invoices/{id}/book. Idempotent (mandatory Idempotency-Key). Dry-runnable.

Use when: You're registering an incoming leverantörsfaktura. Use dry-run first to validate VAT calculations + period-lock state before committing.

Don't use for: Marking an existing SI as paid (use POST /:id/mark-paid). Issuing a credit note (use POST /:id/credit). Customer invoices (different resource).

Pitfalls

  • Idempotency-Key is mandatory.
  • invoice_date must fall within an open fiscal period: a date covered by a locked period or the company-wide bookkeeping lock returns 400 PERIOD_LOCKED.
  • Under faktureringsmetoden the registration JE is posted atomically with the SI row. JE failure aborts the whole call and no SI row is left behind (strict-mode).
  • supplier_id must reference an existing, non-archived supplier in the same company: 404 SUPPLIER_NOT_FOUND otherwise.
  • Duplicate (supplier_id, supplier_invoice_number) returns 409 SI_CREATE_DUPLICATE_INVOICE_NUMBER. Use the credit flow on the original instead of re-registering with a tweaked number.
  • Foreign currency: omit exchange_rate and the server fetches Riksbanken's rate for invoice_date (ML 8 kap 21-23 §). If no rate can be resolved the create is refused with 400 SI_FX_RATE_MISSING rather than stored unconverted: pass exchange_rate explicitly to proceed. A SEK invoice needs no rate and gets total_sek = total.
  • exchange_rate is SEK per 1 unit of the invoice currency and must satisfy 0 < rate < 100000, the same bounds the supplier_invoices CHECK enforces. Out-of-range values return 400 VALIDATION_ERROR; passing an invoice total where a rate belongs is the usual cause.
  • Project/cost-center tagging: pass default_dimensions ({"6":"P001"} = project, {"1":"KS01"} = kostnadsställe) for the whole invoice and/or items[].dimensions per line (per-line wins per key). The registration JE lines are tagged accordingly. When the company has the dimension registry enabled, unknown or archived codes are rejected with 400 DIMENSION_VALIDATION_FAILED — list valid codes via GET /dimensions.
  • Tjänstepension invoices (Avanza etc.): set items[].apply_slp=true on the 741x premium line and the registration JE also books särskild löneskatt (debit 7533 / credit 2514 at 24.26% of the line amount) beyond the payable: 2440 stays at the invoice total. apply_slp on a non-741x account returns 400 SI_CREATE_SLP_INVALID_ACCOUNT.
  • Underlag: upload the invoice PDF with POST /documents first and pass its id as document_id. The document is stored on the invoice and linked to the registration verifikat. A document that is missing, belongs to another company, or is already linked returns 400 SI_CREATE_INVALID_INPUT.
  • Paid privately (eget utlägg): set paid_with_private_funds=true. The invoice is registered as paid and one verifikat books the expense against the payer: the owner (2893 AB, 2018 EF; claimant_name names them) or an employee (employee_id, 2820). payment_date is the out-of-pocket date (defaults to invoice_date). inbox_item_id takes the underlag from an inbox item and is only accepted on this path. Not combinable with reverse charge or periodisering.
  • Periodisering: items[].accrual_period_start + accrual_period_end (and optionally accrual_balance_account, defaulting from the cost account) defer the cost over the period under faktureringsmetoden. Refused under kontantmetoden and with reverse charge.
  • A company that is not VAT-registered cannot book input VAT: a line with vat_rate or vat_amount above 0 returns 400 SI_CREATE_INVALID_INPUT, and an omitted vat_rate defaults to 0. items[].vat_amount overrides line_total × vat_rate (partial deduction, rounding on the supplier's side).

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
supplier_idstringyes
document_idstringno
supplier_invoice_numberstringyes
invoice_datestringyes
due_datestringyes
delivery_datestring | ""no
currency"SEK" | "EUR" | "USD" | "GBP" | "NOK" | "DKK" | "CHF"no
exchange_ratenumberno
vat_treatment"standard_25" | "reduced_12" | "reduced_6" | "reverse_charge" | "export" | "exempt"no
reverse_chargebooleanno
payment_referencestringno
notesstringno
ore_roundingbooleanno
paid_with_private_fundsbooleanno
employee_idstring | nullno
claimant_namestringno
inbox_item_idstring | nullno
payment_datestringno
default_dimensionsobjectno
itemsobject[]yes

Response fields

NameType
idstring
supplier_idstring
arrival_numbernumber
supplier_invoice_numberstring
invoice_datestring
due_datestring
statusstring
currencystring
subtotalnumber
vat_amountnumber
totalnumber
remaining_amountnumber
is_credit_noteboolean
registration_journal_entry_idstring | null
created_atstring

Example request

{
  "supplier_id": "a8f1…",
  "supplier_invoice_number": "2026-1234",
  "invoice_date": "2026-05-10",
  "due_date": "2026-06-09",
  "default_dimensions": {
    "6": "P001"
  },
  "items": [
    {
      "description": "Office supplies",
      "amount": 1000,
      "account_number": "5410",
      "vat_rate": 0.25
    }
  ]
}

Example response

{
  "data": {
    "id": "0e9c…",
    "supplier_id": "a8f1…",
    "arrival_number": 42,
    "supplier_invoice_number": "2026-1234",
    "status": "registered",
    "total": 1250,
    "registration_journal_entry_id": "7b3a…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices/:id/approve

supplier-invoices.approve · scope suppliers:write

Approve a registered or overdue supplier invoice.

Attests a supplier invoice that has not been approved yet (status registered or overdue). The resulting status is approved, or overdue when the invoice is still past its due date. No journal entry is posted here: the registration JE was already booked at :create under accrual (or, under defer_invoice_booking, is posted by POST /supplier-invoices/{id}/book), or is deferred to :mark-paid under cash. Idempotent. Dry-runnable.

Use when: A registered SI has been reviewed and you want to mark it ready for payment. Many AP workflows gate :mark-paid behind an explicit approval step.

Don't use for: Posting a journal entry (already done at :create under accrual). Paying the SI (use :mark-paid). Re-approving an already-approved SI (returns 400 SI_APPROVE_NOT_REGISTERED).

Pitfalls

  • Idempotency-Key is mandatory.
  • Returns 400 SI_APPROVE_NOT_REGISTERED when the invoice is already approved (approved_at set) or sits in a settled status. Use the detail endpoint to inspect status first if unsure.
  • A still-past-due invoice comes back with status "overdue", not "approved": approved_at is the attest marker, the status is derived from the due date.

Risk: low · 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

NameType
idstring
status"approved" | "overdue"
arrival_numbernumber
supplier_invoice_numberstring

Example response

{
  "data": {
    "id": "0e9c…",
    "status": "approved",
    "arrival_number": 42,
    "supplier_invoice_number": "2026-1234"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices/:id/bank-entered

supplier-invoices.mark-bank-entered · scope suppliers:write

Mark a supplier invoice as entered in the internet bank ("inlagd i banken"), or clear the mark.

Records that the payment was entered in the bank by hand, so the invoice stops showing as waiting to be paid. A mark, not a payment: it books nothing and changes no amount or status; the payment is still recorded by :mark-paid or the bank match, and the mark clears itself when one of those lands. entered=true needs an unpaid, payable invoice (approved, overdue, partially_paid; never a credit note); entered=false clears it in any status. Marking an already marked invoice keeps the first timestamp. Idempotent. Dry-runnable.

Use when: The user paid the invoice by typing it into the internet bank (not through a betalfil) and wants the list to say so until the bank transaction arrives.

Don't use for: Recording the payment itself (POST /supplier-invoices/{id}/mark-paid) or payment batches (betalfil).

Pitfalls

  • A registered (unattested), paid or credited invoice, or a credit note, answers 400 SI_BANK_ENTERED_NOT_PAYABLE with details.currentStatus.
  • details.reason race means a payment landed between the read and the write: reload.

Risk: low · 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

NameTypeRequiredDescription
enteredbooleanyestrue marks the invoice as entered in the bank, false clears the mark.

Response fields

NameType
supplier_invoice_idstring
bank_entered_atstring | null

Example request

{
  "entered": true
}

Example response

{
  "data": {
    "supplier_invoice_id": "3b4c…",
    "bank_entered_at": "2026-09-06T10:00:00.000Z"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices/:id/book

supplier-invoices.book · scope suppliers:write

Book a registered supplier invoice that was registered without a verifikat (the deferred Bokför step).

For companies with defer_invoice_booking=true (Registrera men bokför inte): POST /supplier-invoices registers the invoice without posting anything, and this step posts the registration verifikat afterwards (Debit cost accounts per line + 2641 ingående moms, or fiktiv moms for reverse charge; Credit 2440 Leverantörsskulder; periodiserade lines on 17xx with their schedules). Dated on the invoice date. The invoice is claimed with a compare-and-set, so a concurrent book, payment or credit cancels this entry instead of double-posting. The retained source document is anchored to the verifikat. Idempotent. Dry-runnable: the dry run previews the exact lines and writes nothing.

Use when: A supplier invoice is registered, approved or overdue, has no registration_journal_entry_id, and the company books supplier invoices in a separate step (defer_invoice_booking).

Don't use for: Paid or partially paid invoices (their payment booked them in full), credit notes, or any supplier invoice under kontantmetoden (booked at payment via :mark-paid).

Pitfalls

  • An invoice that already has a registration_journal_entry_id answers 400 SI_BOOK_ALREADY_BOOKED.
  • Status other than registered, approved or overdue answers 400 SI_BOOK_INVALID_STATUS with details.currentStatus.
  • Under kontantmetoden answers 400 SI_BOOK_CASH_METHOD.
  • A locked or closed period, or an invoice date on or before the company lock date (bookkeeping_locked_through), answers 400 PERIOD_LOCKED with details.reason, details.fiscal_period_id and details.invoice_date. Nothing is generated, so no voucher number is spent: unlock the period (only if the user asked for that correction) and retry.
  • No open fiscal year covering the invoice date answers 400 SI_BOOK_NO_FISCAL_PERIOD.
  • Booking does not attest the invoice: :approve is a separate step and may come before or after.

Risk: high · 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

NameType
supplier_invoiceobject
journal_entry_idstring

Example response

{
  "data": {
    "supplier_invoice": {
      "id": "3b4c…",
      "arrival_number": 118,
      "supplier_invoice_number": "55012",
      "status": "approved",
      "invoice_date": "2026-09-03",
      "due_date": "2026-10-03",
      "currency": "SEK",
      "total": 6250,
      "registration_journal_entry_id": "6d7e…"
    },
    "journal_entry_id": "6d7e…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices/:id/credit

supplier-invoices.credit · scope suppliers:write

Issue a credit note for a supplier invoice.

Creates a kreditfaktura that reverses the original supplier invoice. Under accrual the reversing JE is posted atomically (Debit 2440 / Credit expense + Credit 2641). The original status flips to credited. Strict-mode: any failure rolls back the credit-note row. Idempotent. Dry-runnable.

Use when: You need to nullify a registered, approved, partially_paid, or paid supplier invoice: for a returned shipment, an over-invoice, or a vendor dispute resolution. Use dry-run to confirm the totals first.

Don't use for: Editing line items on an unchanged invoice (use PATCH on registered SIs). Crediting an already-credited SI (returns 409 SI_CREDIT_ALREADY_CREDITED). Reversing a v1-issued credit (no v1 endpoint today: use the dashboard).

Pitfalls

  • Idempotency-Key is mandatory.
  • Today's date is used as the credit-note invoice_date. It must fall in an open fiscal period: locked period returns 400 SI_CREDIT_PERIOD_LOCKED.
  • Cash basis (kontantmetoden): no reversing JE is posted: recognition is deferred until a refund transaction is booked. The credit-note row is still created so the AP audit trail stays consistent.
  • The original SI is flipped to credited regardless of how much of it was already paid; reconcile the bank refund via the transactions endpoints.

Risk: high · 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

NameType
credit_note_idstring
original_idstring
arrival_numbernumber
supplier_invoice_numberstring
registration_journal_entry_idstring | null

Example response

{
  "data": {
    "credit_note_id": "4d2a…",
    "original_id": "0e9c…",
    "arrival_number": 43,
    "supplier_invoice_number": "KREDIT-2026-1234",
    "registration_journal_entry_id": "9c2f…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices/:id/mark-paid

supplier-invoices.mark-paid · scope suppliers:write

Record a payment against a supplier invoice.

Books the payment journal entry (Debit 2440 / Credit the payment account under accrual; or Debit expense + Debit 2641 / Credit the payment account under cash) and flips the SI status to paid (full settlement) or partially_paid. The payment account is payment_account when supplied, otherwise 1930 Företagskonto. Strict-mode: a JE failure aborts before any SI mutation. Idempotent. Dry-runnable.

Use when: You paid a registered or approved leverantörsfaktura through a channel other than the synced bank flow. For bank-matched payments use POST /transactions/{id}/match-supplier-invoice instead: that path also reconciles the bank line.

Don't use for: Refunding a payment (the public API does not expose unmark-paid; credit the SI instead). Paying a credited or already-paid SI (returns 409 SI_PAID_ALREADY).

Pitfalls

  • Idempotency-Key is mandatory.
  • payment_date must fall in an open fiscal period: locked period returns 400 PERIOD_LOCKED.
  • exchange_rate_difference (SEK delta vs the booked rate at registration) is required for foreign-currency SIs to book the FX gain/loss to 3960 / 7960. Omitting it on a non-SEK SI under accrual mis-books FX.
  • Strict-mode: a JE creation failure ABORTS before the status flip. There is no partial-state recovery banner: retry the call.
  • Cash basis (kontantmetoden) recognizes the expense + ingående moms HERE, not at :create.
  • Cash basis + öresavrundning: a SEK invoice with ore_rounding on and an öre-bearing total is paid in whole kronor, so the generated entry credits the payment account with the rounded amount and books the residual on 3740 (no VAT). amount, paid_amount and remaining_amount stay in exact öre. Invoices whose rounding is already an invoice row on 3740 have a whole-krona total and are unaffected.
  • payment_account picks the BAS account credited for the payment (1930 Företagskonto when omitted, on both the accrual and the cash path). It must be active in the chart of accounts: an unknown or deactivated account returns 400 ACCOUNTS_NOT_IN_CHART and books nothing. Beyond that it is credited exactly as given, with no range check: 19xx bank or kassa is the ordinary choice, but 1630 (betald via skattekontot) and 2893 / 2018 / 2820 (someone else paid, utlägg) are equally valid, so choosing an account that does not represent where the money actually came from is the caller's error to avoid. Unlike the dashboard dialog, this endpoint does not read the company's last-used payment account: omitting the field always means 1930.
  • Duplicate-payment guard: on a full settlement, if a business bank transaction of the same amount around payment_date carries the supplier name (first distinctive token, so abbreviated bank text such as "HI3G" for Hi3G Access AB counts), returns 409 SI_PAID_LIKELY_DUPLICATE with candidate transactions. A candidate with match_reason already_booked is a bank row that is ALREADY a verifikat: do not pay the invoice, correct the double booking instead. Retry with force: true only after the user confirms, and with a fresh Idempotency-Key (the original is body-hash bound). Also evaluated under dry-run. A forced full settlement is recorded in behandlingshistorik together with the candidates the guard would have flagged.

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
amountnumberno
payment_datestringno
exchange_rate_differencenumberno
notesstringno
forcebooleanno
payment_accountstringno
linesobject[]no

Response fields

NameType
idstring
status"paid" | "partially_paid"
totalnumber
paid_amountnumber
remaining_amountnumber
paid_atstring | null
payment_journal_entry_idstring | null

Example request

{
  "payment_date": "2026-05-13"
}

Example response

{
  "data": {
    "id": "0e9c…",
    "status": "paid",
    "total": 1250,
    "paid_amount": 1250,
    "remaining_amount": 0,
    "paid_at": "2026-05-13",
    "payment_journal_entry_id": "7b3a…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/supplier-invoices/:id/uncredit

supplier-invoices.uncredit · scope suppliers:write

Undo the credit of a supplier invoice ("Ångra kreditering"): storno the credit note's verifikat and restore the invoice.

For an original supplier invoice with status credited: posts a storno cancelling the live credit note's verifikat (dated on that verifikat's date, never an edit or delete), marks the credit note reversed (the row is kept for the archive and the ankomstnummer series), and restores the original's status and remaining amount from its payments (paid, partially_paid, overdue, approved, or registered when it has no verifikat). The invoice can be credited again afterwards. An invoice that is not credited is an idempotent no-op (changed=false). Dry-runnable: the preview names the storno and the restored status.

Use when: A supplier invoice was credited by mistake and the credit should be taken back.

Don't use for: Crediting an invoice (POST /supplier-invoices/{id}/credit), deleting an unbooked invoice (DELETE /supplier-invoices/{id}) or reversing an arbitrary verifikat.

Pitfalls

  • Pass the ORIGINAL invoice id, not the credit note's.
  • The credit note's verifikat must lie in an open, unlocked period: otherwise the dry run answers 400 PERIOD_LOCKED and the commit 400 SI_UNCREDIT_FAILED.
  • A credit verifikat already reversed by hand is fine: the row cleanup still runs and reversal_entry_id is null.

Risk: high · 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
supplier_invoiceobject
reversal_entry_idstring | nullThe storno verifikat, when the credit note had a posted one.
reversed_credit_note_idstring | null
changedbooleanFalse when the invoice was not credited (nothing done).

Example response

{
  "data": {
    "supplier_invoice": {
      "supplier_invoice_id": "3b4c…",
      "arrival_number": 118,
      "supplier_invoice_number": "55012",
      "status": "approved",
      "invoice_date": "2026-09-03",
      "due_date": "2026-10-03",
      "currency": "SEK",
      "total": 6250,
      "remaining_amount": 6250,
      "registration_journal_entry_id": "6d7e…"
    },
    "reversal_entry_id": "9a8b…",
    "reversed_credit_note_id": "5c6d…",
    "changed": true
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

PATCH /api/v1/companies/:companyId/supplier-invoices/:id

supplier-invoices.update · scope suppliers:write

Update a registered supplier invoice.

Patches a supplier invoice with the supplied fields. Only allowed on registered status: once approved, paid, or credited, the record is effectively immutable from the API's perspective. Idempotent (mandatory Idempotency-Key). Dry-runnable.

Use when: You need to adjust due_date, or attach a payment reference / notes to a registered SI before approval. Use dry-run to confirm the merged state first.

Don't use for: Editing line items (immutable: credit the SI and register a new one). Changing status (use action verbs). Approved/paid/credited SIs (returns 400 SI_NOT_DRAFT). invoice_date / supplier_invoice_number on an SI that already has a registration verifikat (returns 400 SI_EDIT_VERIFIKAT_LOCKED).

Pitfalls

  • Returns 400 SI_NOT_DRAFT when current status !== "registered".
  • invoice_date and supplier_invoice_number are on the posted registration verifikat (entry_date and description). Once registration_journal_entry_id is set, patching them returns 400 SI_EDIT_VERIFIKAT_LOCKED: correct the entry via a rättelse (gnubok_correct_entry) or credit the SI and re-register. Resending the unchanged value is accepted.
  • Patching a field never re-posts the registration JE.

Risk: low · 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
supplier_invoice_numberstringno
invoice_datestringno
due_datestringno
delivery_datestring | ""no
payment_referencestringno
notesstringno

Response fields

NameType
idstring
supplier_idstring
arrival_numbernumber
supplier_invoice_numberstring
invoice_datestring
due_datestring
received_datestring
delivery_datestring | null
statusstring
currencystring
exchange_ratenumber | null
subtotalnumber
vat_amountnumber
totalnumber
vat_treatmentstring
reverse_chargeboolean
paid_amountnumber
remaining_amountnumber
is_credit_noteboolean
credited_invoice_idstring | null
registration_journal_entry_idstring | null
payment_journal_entry_idstring | null
notesstring | null
created_atstring
updated_atstring

Example request

{
  "payment_reference": "OCR-1234567890"
}

Example response

{
  "data": {
    "id": "0e9c…",
    "payment_reference": "OCR-1234567890"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

PATCH /api/v1/companies/:companyId/supplier-invoices/:id/items/:itemId

supplier-invoices.update-item-account · scope suppliers:write

Move one supplier-invoice line to another account, correcting the registration verifikat inline.

Changes the account of one line on an unsettled supplier invoice (registered, approved, overdue). When the invoice has a posted registration verifikat, the same verifikat is corrected inside itself through the inline rättelse (the old line is struck and replaced, split when the verifikat carries one line per account), logged with who and when (BFL 5 kap 5 §); that is only allowed in an open, unlocked period. Without a verifikat only the line changes. A standard BAS account missing from the chart is added. Idempotent. Dry-runnable: the preview carries the planned rättelse lines.

Use when: A supplier invoice line was booked on the wrong cost account (e.g. 6580 instead of 6550) and the period is still open.

Don't use for: Settled invoices (paid, credited), locked or closed periods (storno through POST /journal-entries/{id}/reverse and a new verifikat), or changing amounts or VAT.

Pitfalls

  • A settled invoice answers 409 SI_ITEM_ACCOUNT_SETTLED.
  • A locked or closed period answers 409 JOURNAL_RATTELSE_PERIOD_LOCKED: past a lock, storno is the only lawful correction.
  • When the verifikat was already corrected by hand and holds no matching line on the old account, the answer is 409 SI_ITEM_ACCOUNT_NO_MATCHING_LINE and nothing changes.
  • account_number is a STRING ("6550"), never a number.

Risk: high · 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

NameTypeRequiredDescription
account_numberstringyesThe BAS account to move the line to, as a string, e.g. "6550".

Response fields

NameTypeDescription
changedbooleanFalse when the line was already on that account.
correctedboolean (optional)True when the registration verifikat was corrected inline.

Example request

{
  "account_number": "6550"
}

Example response

{
  "data": {
    "changed": true,
    "corrected": true
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

DELETE /api/v1/companies/:companyId/supplier-invoices/:id

supplier-invoices.delete · scope suppliers:write

Delete an unbooked, unpaid supplier invoice (no verifikat, no payment).

Removes a supplier invoice that never reached the books: status registered, approved or overdue, no registration verifikat, no payment, no accrual schedule and no payment-batch row. Its lines go with it. A booked invoice is never deleted: withdraw it with a credit note (POST /supplier-invoices/{id}/credit), which keeps both verifikat in the audit trail (BFL 5 kap 5 §). Idempotent. Dry-runnable.

Use when: A supplier invoice was registered by mistake (a duplicate, the wrong company, a quote) under defer_invoice_booking or kontantmetoden, so no verifikat exists yet.

Don't use for: Booked invoices (credit them), credit notes (undo the credit on the original: POST /supplier-invoices/{id}/uncredit) or discarding an inbox item (DELETE /inbox-items/{id}).

Pitfalls

  • An invoice with a registration verifikat, a payment or an accrual schedule answers 400 SI_DELETE_HAS_BOOKING with details.reason (registration_journal_entry, payments, accrual_schedule).
  • A credit note answers 400 SI_DELETE_CREDIT_NOTE; paid, partially paid or credited invoices answer 400 SI_DELETE_INVALID_STATUS.
  • An invoice in a payment batch (even a cancelled one) answers 409 SI_DELETE_IN_PAYMENT_BATCH: the batch rows document the payment instruction.
  • The ankomstnummer the invoice held is not reused.

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

NameType
supplier_invoice_idstring
deletedtrue

Example response

{
  "data": {
    "supplier_invoice_id": "3b4c…",
    "deleted": true
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}