Menu

Inbox items

The invoice inbox: list and read items, correct the extracted fields, convert an item to a supplier invoice, unmatch a transaction, stamp or delete an item.

Endpoints


GET /api/v1/companies/:companyId/inbox-items

inbox-items.list · scope documents:read

List invoice-inbox items (Underlag) with a summary of what was read from each.

Returns inbox items newest first: how each arrived, the document, the vendor, total, currency and date read from it, and its links (transaction match, supplier invoice, verifikat). processed is true once any link exists; unprocessed_only=true returns only the items still needing handling. Cursor pagination: pass next_cursor back as cursor; null on the last page. The full reading and the e-mail text are on GET /inbox-items/{id}.

Use when: You work the inbox: receipts and invoices waiting to be matched, converted or booked.

Don't use for: The document archive as a whole (GET /documents) or supplier invoices already registered (GET /supplier-invoices).

Pitfalls

  • amount is in the document's currency: compare with a transaction's amount only after converting.
  • A matched item whose transaction is booked can still have underlag_status unlinked: the document did not reach the verifikat.
  • The page is in data.inbox_items 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"received" | "error"noerror = extraction failed.
unprocessed_only"true" | "false"notrue = only items with no transaction match, supplier invoice or verifikat yet.
cursorstringnonext_cursor from the previous page. Omit for the first page.
limitnumbernoPage size, 1-100 (default 50).

Response fields

NameType
inbox_itemsobject[]
next_cursorstring | null

Example response

{
  "data": {
    "inbox_items": [
      {
        "inbox_item_id": "1b2c…",
        "status": "received",
        "source": "email",
        "created_at": "2026-09-02T07:41:10Z",
        "document_id": "4f1c…",
        "kind_hint": null,
        "vendor_name": "Clas Ohlson AB",
        "amount": 499,
        "currency": "SEK",
        "invoice_date": "2026-09-01",
        "processed": false,
        "matched_supplier_id": null,
        "matched_transaction_id": null,
        "matched_transaction_journal_entry_id": null,
        "created_supplier_invoice_id": null,
        "created_journal_entry_id": null,
        "underlag_status": null,
        "email_from": "kvitto@clasohlson.se",
        "email_subject": "Ditt kvitto",
        "email_received_at": "2026-09-02T07:41:02Z",
        "error_message": null
      }
    ],
    "next_cursor": null
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

GET /api/v1/companies/:companyId/inbox-items/:id

inbox-items.get · scope documents:read

Read one inbox item with its full reading and e-mail text.

Returns the item's summary plus the complete extracted_data (supplier, invoice, totals, line items, VAT breakdown), the e-mail body text and the document's file name. When the item has no reading of its own, the document's reading stands in.

Use when: You are about to correct the reading, convert the item to a supplier invoice or match it, and need every field.

Don't use for: Scanning the queue (GET /inbox-items) or downloading the file (GET /documents/{id}/download).

Pitfalls

  • extracted_data is what was read, not what was booked: check it against the document before converting.
  • email_body_text can carry personal data: do not copy it into notes or descriptions.
  • An id from another company answers 404 INBOX_ITEM_NOT_FOUND.

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

Response fields

NameTypeDescription
inbox_item_idstring
statusstringreceived or error (extraction failed).
sourcestringHow it arrived: email, upload, api, whatsapp, ...
created_atstring
document_idstring | null
kind_hintstring | nullSender-declared kind from a +lev / +ver address tag.
vendor_namestring | nullSupplier name as read from the document.
amountnumber | nullTotal as read from the document, in its currency.
currencystring | null
invoice_datestring | null
processedbooleanTrue once the item has a terminal link: a transaction match, a supplier invoice or a verifikat.
matched_supplier_idstring | null
matched_transaction_idstring | null
matched_transaction_journal_entry_idstring | nullThe verifikat that booked the matched transaction, when it is booked.
created_supplier_invoice_idstring | null
created_journal_entry_idstring | null
underlag_status"anchored" | "unlinked" | "unlinked_locked" | "anchored_elsewhere" | "unknown" | nullFor a matched item whose transaction is booked: whether THIS item's document reached that verifikat (anchored) or not. Null otherwise.
email_fromstring | null
email_subjectstring | null
email_received_atstring | null
error_messagestring | null
extracted_dataobject | null
extraction_skippedboolean
email_body_textstring | null
file_namestring | null
updated_atstring

Example response

{
  "data": {
    "inbox_item_id": "1b2c…",
    "status": "received",
    "source": "email",
    "created_at": "2026-09-02T07:41:10Z",
    "document_id": "4f1c…",
    "kind_hint": null,
    "vendor_name": "Clas Ohlson AB",
    "amount": 499,
    "currency": "SEK",
    "invoice_date": "2026-09-01",
    "processed": false,
    "matched_supplier_id": null,
    "matched_transaction_id": null,
    "matched_transaction_journal_entry_id": null,
    "created_supplier_invoice_id": null,
    "created_journal_entry_id": null,
    "underlag_status": null,
    "email_from": "kvitto@clasohlson.se",
    "email_subject": "Ditt kvitto",
    "email_received_at": "2026-09-02T07:41:02Z",
    "error_message": null,
    "extracted_data": {
      "supplier": {
        "name": "Clas Ohlson AB"
      },
      "invoice": {
        "invoiceDate": "2026-09-01",
        "currency": "SEK"
      },
      "totals": {
        "total": 499
      }
    },
    "extraction_skipped": false,
    "email_body_text": null,
    "file_name": "kvitto.pdf",
    "updated_at": "2026-09-02T07:41:30Z"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/inbox-items/:id/convert

inbox-items.convert-to-supplier-invoice · scope suppliers:write

Register a supplier invoice from an inbox item, with its document as underlag.

Registers a supplier invoice (status registered, next ankomstnummer) from the given lines, attaches the item's document as underlag and marks the item converted. A company that books on registration gets the registration verifikat at once (cost and 2641 against 2440, with periodisering and särskild löneskatt where the lines ask); a company that defers booking gets none until the invoice is booked. A non-SEK invoice without exchange_rate gets Riksbanken's rate for invoice_date. Idempotent. Dry-runnable: the preview computes the invoice without an ankomstnummer.

Use when: An inbox item is a supplier invoice the company will pay later (leverantörsskuld).

Don't use for: A receipt the company already paid (book it against the bank transaction), a purchase paid privately (POST /expense-claims), or registering an invoice without an inbox item (POST /supplier-invoices).

Pitfalls

  • An item already converted returns 409 INBOX_ITEM_ALREADY_CONVERTED; a supplier invoice number the supplier already has returns 409 SI_CREATE_DUPLICATE_INVOICE_NUMBER with details.existing.
  • amount is per line EXCLUDING VAT; VAT is computed from vat_rate. Per-line vat_amount, dimensions and private-payment fields are not accepted here.
  • No fiscal year for invoice_date returns SI_CREATE_NO_FISCAL_PERIOD and registers nothing.
  • account_number is a STRING ("6110"), never a number.

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

NameTypeRequiredDescription
supplier_idstringyesThe supplier (supplier_id from GET /suppliers).
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
itemsobject[]yes

Response fields

NameTypeDescription
supplier_invoice_idstring
arrival_numbernumber | nullAnkomstnummer.
statusstring
currencystring
totalnumber
total_seknumber | null
registration_journal_entry_idstring | nullThe registration verifikat, or null when the company defers booking.
inbox_item_idstring

Example request

{
  "supplier_id": "7c1d…",
  "supplier_invoice_number": "F-2026-118",
  "invoice_date": "2026-09-01",
  "due_date": "2026-09-30",
  "items": [
    {
      "description": "Kontorsmaterial",
      "amount": 399.2,
      "account_number": "6110",
      "vat_rate": 0.25
    }
  ]
}

Example response

{
  "data": {
    "supplier_invoice_id": "a9e0…",
    "arrival_number": 118,
    "status": "registered",
    "currency": "SEK",
    "total": 499,
    "total_sek": 499,
    "registration_journal_entry_id": "9c1e…",
    "inbox_item_id": "1b2c…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/inbox-items/:id/match-supplier

inbox-items.match-supplier · scope documents:write

Set which supplier an inbox item comes from.

Sets the item's matched supplier, the supplier the conversion to a supplier invoice (POST /inbox-items/{id}/convert) uses when the request names none. A hint only: nothing is registered or booked. Picking another supplier later replaces it. Idempotent. Dry-runnable.

Use when: The reading named the supplier ambiguously or not at all, and the right supplier exists in the register (GET /suppliers).

Don't use for: Creating a supplier (POST /suppliers) or registering the invoice (POST /inbox-items/{id}/convert, which also accepts supplier_id directly).

Pitfalls

  • The supplier must belong to the same company: otherwise 404 SUPPLIER_NOT_FOUND.
  • An item already converted keeps the supplier its supplier invoice has; this only changes the item's hint.

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
supplier_idstringyesThe supplier id (from GET /suppliers).

Response fields

NameType
inbox_item_idstring
matched_supplier_idstring

Example request

{
  "supplier_id": "8a9b…"
}

Example response

{
  "data": {
    "inbox_item_id": "1b2c…",
    "matched_supplier_id": "8a9b…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/inbox-items/:id/match-transaction

inbox-items.match-transaction · scope documents:write

Pair an inbox item with the bank transaction it documents.

Sets the item's matched transaction and, when the transaction has no document yet, pins the item's document on it (an existing pin is never replaced). When the transaction is already booked, the item is completed against that verifikat: the document becomes its underlag. Release with POST /inbox-items/{id}/unmatch-transaction. Idempotent. Dry-runnable.

Use when: A receipt or invoice in the inbox belongs to a bank transaction (typically a card purchase) and should travel with it to booking.

Don't use for: Registering a supplier invoice from the item (POST /inbox-items/{id}/convert) or attaching an arbitrary document to a transaction (POST /transactions/{id}/attach-document).

Pitfalls

  • The transaction must belong to the same company: otherwise 404 TX_CATEGORIZE_TX_NOT_FOUND.
  • A transaction that already carries another document keeps it (details in the dry run: transaction_has_other_document).
  • Matching a booked transaction in a locked period links the document best-effort; check the verifikat afterwards.

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
transaction_idstringyesThe bank transaction id (from GET /transactions).

Response fields

NameType
inbox_item_idstring
matched_transaction_idstring

Example request

{
  "transaction_id": "1f2e…"
}

Example response

{
  "data": {
    "inbox_item_id": "1b2c…",
    "matched_transaction_id": "1f2e…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/inbox-items/:id/stamp

inbox-items.stamp · scope documents:write

Mark an inbox item as consumed by a journal entry.

Sets created_journal_entry_id on an invoice_inbox_items row so the item drops out of the active inbox todo list. Use when the document was linked to a JE via a separate call and you need to close the inbox item independently.

Use when: An inbox document has already been attached to a verifikation (via documents link) but the inbox item itself was not stamped at link time: e.g. when using the v1 link endpoint without inbox_item_id.

Don't use for: Creating a new journal entry from an inbox item: use the invoice-inbox extension book-direct route for that.

Pitfalls

  • Idempotency-Key is mandatory.
  • The inbox item and journal_entry_id must both belong to the caller's company.
  • Stamping with a different journal_entry_id than the one already set returns CONFLICT: the item is already resolved.

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

Request body

NameTypeRequired
journal_entry_idstringyes

Response fields

NameType
idstring
created_journal_entry_idstring

Example request

{
  "journal_entry_id": "dcccb3c5-b44a-4536-82fa-f0b9bb77f900"
}

Example response

{
  "data": {
    "id": "4d2fcdbb-13b3-4ff3-911f-a4cc82f1f6db",
    "created_journal_entry_id": "dcccb3c5-b44a-4536-82fa-f0b9bb77f900"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

POST /api/v1/companies/:companyId/inbox-items/:id/unmatch-transaction

inbox-items.unmatch-transaction · scope documents:write

Release an inbox item's bank transaction match.

Clears the item's matched transaction, and the transaction's document pin when it is still this item's document (a document from another source stays). Answers the released transaction. The item returns to the work queue. Idempotent. Dry-runnable.

Use when: The item was paired with the wrong bank transaction, or should be paired again.

Don't use for: Taking a document off a transaction directly (POST /transactions/{id}/detach-document) or undoing a booked verifikat (storno).

Pitfalls

  • Once the transaction is booked with this document as underlag, the transaction pin cannot be cleared (the document is räkenskapsinformation); the item's own match is released regardless.
  • An item with no match answers success with released_transaction_id null.

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.

Response fields

NameType
inbox_item_idstring
matched_transaction_idunknown
released_transaction_idstring | null

Example response

{
  "data": {
    "inbox_item_id": "1b2c…",
    "matched_transaction_id": null,
    "released_transaction_id": "1f2e…"
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

PATCH /api/v1/companies/:companyId/inbox-items/:id

inbox-items.update-extracted-data · scope documents:write

Correct fields of an inbox item's reading (supplier, invoice, totals).

Merges the given fields into the item's extracted_data: supplier (name, orgNumber, vatNumber, address, bankgiro, plusgiro), invoice (invoiceNumber, invoiceDate, dueDate, paymentReference, currency) and totals (subtotal, vatAmount, total). Fields not named are kept, line items and the VAT breakdown included; null clears a field. A hand-set total becomes a verified total. Answers the merged reading. Idempotent. Dry-runnable.

Use when: The reading got a field wrong (total, date, invoice number) before the item is converted or matched.

Don't use for: Replacing the whole reading from your own extraction pipeline (MCP gnubok_set_inbox_extracted_data), or changing a registered supplier invoice.

Pitfalls

  • An item already converted to a supplier invoice returns 409 INBOX_ITEM_EDIT_LOCKED.
  • A concurrent edit returns 409 INBOX_ITEM_EDIT_CONFLICT: read the item again and retry.
  • Dates are YYYY-MM-DD; currency is a 3-letter ISO 4217 code.
  • An enskild firma's orgNumber is a personnummer: only send it when it is on the document.

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
supplierobjectno
invoiceobjectno
totalsobjectno

Response fields

NameType
inbox_item_idstring
extracted_dataobject

Example request

{
  "totals": {
    "total": 499
  },
  "invoice": {
    "invoiceDate": "2026-09-01"
  }
}

Example response

{
  "data": {
    "inbox_item_id": "1b2c…",
    "extracted_data": {
      "totals": {
        "total": 499
      },
      "invoice": {
        "invoiceDate": "2026-09-01"
      }
    }
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}

DELETE /api/v1/companies/:companyId/inbox-items/:id

inbox-items.delete · scope documents:write

Discard an inbox item that was never converted or booked.

Removes the inbox item (its e-mail metadata and reading). The document it carried stays in the archive with its own deletion rule (DELETE /documents/{id}). Refused once the item became a supplier invoice or was booked. Idempotent. Dry-runnable.

Use when: Spam, a duplicate delivery or a non-accounting e-mail landed in the inbox.

Don't use for: Deleting the document itself (DELETE /documents/{id}) or undoing a supplier invoice or verifikat.

Pitfalls

  • A converted item returns 409 INBOX_ITEM_DELETE_CONVERTED; a booked one 409 INBOX_ITEM_DELETE_BOOKED.
  • Cannot be undone: the e-mail metadata and the reading are gone.

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
inbox_item_idstring
deletedtrue

Example response

{
  "data": {
    "inbox_item_id": "1b2c…",
    "deleted": true
  },
  "meta": {
    "request_id": "req_…",
    "api_version": "2026-05-12"
  }
}