Skip to content

Expense

Expense Types

Receipt: Expenses that are already paid (e.g., credit card purchases, cash payments). These are automatically marked as approved and paid when uploaded, and require a payment method.

Invoice: Unpaid expense invoices from vendors that need to be settled. These start as drafts requiring approval and can have outgoing payments managed through Expense Payments.

Allowed query parameters

Filters

FilterTypeDescription
searchStringFull-text search
typeStringFilter based on type:
receipt, invoice
statusStringFilter based on status:
draft, open, approved, overdue, paid
date_rangeDateRangeFilter by issued date
<start-date>,<end-date>
projectIdFilter by Project Id
companyIdFilter by Company Id
memberIdFilter by Member Id
created_byIdFilter by creator Member Id
updated_byIdFilter by updater Member Id
is_lockedBooleanShow only locked expenses
is_billableBooleanShow only billable expenses
is_budget_relevantBooleanShow only budget relevant expenses
account_payableStringFilter by account payable
tagsArrayFilter by tag IDs
reconciledBooleanFilter by reconciliation status
exportedBooleanFilter by export status (ignores false)
total_rangeRangeFilter by total amount range
net_total_rangeRangeFilter by net total amount range
has_bank_transaction_matchBooleanFilter by bank transaction match status
custom_fieldKey,StringFilter by Custom Field (eg. my_internal_id,42)

Sorting

name, created_at, issued_at, due_at, paid_at, approved_at, total, payment_method

Includes

project.company.media, member.user.media, createdBy.user.media, updatedBy.user.media, tag, media, invoice, tags, paymentMethod.account, comments.commentator.media, payments.bankAccount, payments.bankTransactionMatch.bankTransaction.bankAccount, bankTransactionMatch.bankTransaction.bankAccount, activities

Upload and process an expense

post
/expense/upload

This endpoint allows you to upload a receipt or invoice file (PDF or image) and automatically extract expense data using AI processing, Swiss QR code parsing, and image optimization.

File Upload Process

Before using this endpoint, you need to upload your file using the File Upload process. The uuid and key parameters below come from the signed upload URL response.

Attribute (* required)TypeDescription
uuid *StringUUID from the signed upload URL response
nameString?Optional expense name
content_type *StringFile MIME type: application/pdf, image/jpeg, image/jpg, image/png
key *StringStorage key from the signed upload URL response (max 10MB)
data.type *StringExpense type: receipt or invoice
data.payment_method_idString?Payment method ID (for receipts)

Processing workflow

  1. File Upload: The file is validated and stored
  2. Expense Creation: A basic expense record is created with is_processing: true
  3. Background Processing: Three jobs are queued:
    • Swiss QR Code Parsing: Extracts payment information from Swiss QR bills
    • AI-Powered Extraction: Uses AI to extract expense details (vendor, amounts, dates, etc.)
    • Image Optimization: Auto-crops and optimizes the uploaded image
  4. Completion: When processing finishes, is_processing is set to false and an event is broadcast

Receipt vs Invoice behavior

  • Receipts: Automatically marked as approved and paid (dates set to current date)
  • Invoices: Created as draft expenses requiring manual approval

Example response

json
// HTTP 201 Created
{
    "expense": {
        "id": "v7rnRjBn9o",
        "type": "receipt",
        "name": "Restaurant Receipt",
        "is_processing": true,
        "approved_at": "2024-01-15T00:00:00.000000Z",
        "paid_at": "2024-01-15T00:00:00.000000Z"
        // ... other expense fields
    },
    "batch_id": "9c3b5e4d-8f2a-4b6c-9d1e-2f3a4b5c6d7e",
    "parsedProperties": [],
    "error": null
}

The batch_id can be used to track the processing status using the Background Jobs monitoring endpoint. Once processing completes, the expense will have extracted data populated and is_processing will be false.

Create an expense

post
/expense
Attribute (* required)TypeDescription
name *StringExpense title (max 255 chars)
descriptionString?Expense description
typeString?Expense type (invoice, receipt)
project_idString?A Project Id
issued_at *DateIssue date (must be in valid fiscal year)
due_atDate?Due date (must be after or equal to issued_at)
approved_atDate?Approval date
paid_atDate?Pay date
biller_addressString?Creditor address
biller_ibanString?Creditor IBAN (validated)
biller_bicString?Creditor BIC (max 11 chars)
biller_additional_infoString?Additional biller information
referenceString?Payment reference
account_payableString?Account payable
invoice_idString?Associated Invoice Id
tag_idString?A Tag Id
payment_method_idString?Payment method Id
currencyString?Currency code
is_budget_relevantBoolean?If expense is budget relevant
is_billableBoolean?If expense is client billable
is_lockedBoolean?If expense is locked
has_acquisition_taxBoolean?If expense has acquisition tax
positionsArray?An array of position objects (sum must be >= 0)
positions.*.total *FloatPosition amount
positions.*.vat_rate *FloatPosition VAT percentage (0-100)
positions.*.mode *StringPosition mode (inclusive, exclusive)
tagsArray?Array of tag IDs
custom_fieldsObject?Custom data as Custom Fields

Example response

json
// HTTP 201 Created
{
    // a expense object
}

Update an expense

put
/expense/{id}
AttributeTypeDescription
nameString?Expense title (max 255 chars)
descriptionString?Expense description
typeString?Expense type (invoice, receipt)
project_idString?A Project Id
issued_atDate?Issue date (must be in valid fiscal year)
due_atDate?Due date
approved_atDate?Approval date
paid_atDate?Pay date
biller_addressString?Creditor address
biller_ibanString?Creditor IBAN (validated)
biller_bicString?Creditor BIC (max 11 chars)
biller_additional_infoString?Additional biller information
referenceString?Payment reference
account_payableString?Account payable
payment_method_idString?Payment method Id (prohibited for invoice, required for receipt)
currencyString?Currency code
is_budget_relevantBoolean?If expense is budget relevant
is_billableBoolean?If expense is client billable
is_lockedBoolean?If expense is locked
is_exportedBoolean?If expense is exported
has_acquisition_taxBoolean?If expense has acquisition tax
positionsArray?An array of position objects (sum must be >= 0)
positions.*.total *FloatPosition amount
positions.*.vat_rate *FloatPosition VAT percentage (0-100)
positions.*.modeString?Position mode (inclusive, exclusive)
tagsArray?Array of tag IDs
extracted_dataObject?Extracted data object
extracted_data.typeString?Extracted expense type
custom_fieldsObject?Custom data as Custom Fields

Example response

json
// HTTP 200 OK
{
    // a expense object
}

Retrieve an expense

get
/expense/{id}

Example response

json
// HTTP 200 OK
{
    "id": "v7rnRjBn9o",
    "number": "E-2021-0001",
    "type": "invoice",
    "project_id": "AG52olvLXP",
    "name": "Kreditkartenabrechnung",
    "description": "Mastercard",
    "issued_at": "2021-07-13 00:00:00",
    "due_at": "2021-08-02 00:00:00",
    "approved_at": "2021-09-21T23:27:57.000000Z",
    "paid_at": null,
    "reference": "10 00000 00000 06096 37449 80405",
    "account_payable": null,
    "amount": 540.05,
    "total": 540.05,
    "total_with_vat": 540.05,
    "tax_total": 0,
    "currency": "CHF",
    "has_acquisition_tax": false,
    "positions": [
        {
            "mode": "inclusive",
            "total": 540.05,
            "vat_rate": 0,
            "account_no": null
        }
    ],
    "total_paid": 0,
    "total_open": 540.05,
    "biller_uid": null,
    "biller_uid_formatted": null,
    "biller_address": "Cembra MoneyBank\r\nP.P.\r\n8048 Zürich\r\nCH",
    "biller_address_lines": ["Cembra MoneyBank", "P.P.", "8048 Zürich", "CH"],
    "biller_address_html": "Cembra MoneyBank<br/>\r\nP.P.<br/>\r\n8048 Zürich<br/>\r\nCH",
    "biller_iban": "CH7230114000000018007",
    "biller_iban_formatted": "CH72 3011 4000 0000 1800 7",
    "biller_bic": null,
    "biller_additional_info": null,
    "has_qr_iban": true,
    "has_qr_reference": true,
    "has_scor_reference": false,
    "missing_bank_payment_info": [],
    "bank_payment_type": "qrr",
    "is_paid": false,
    "is_approved": true,
    "is_budget_relevant": false,
    "is_billable": true,
    "is_locked": false,
    "is_overdue": false,
    "is_exported": false,
    "is_processing": false,
    "days_overdue": 0,
    "created_at": "2021-09-22T09:27:20.000000Z",
    "updated_at": "2022-04-21T12:46:55.000000Z",
    "project": null,
    "member": null,
    "created_by": null,
    "updated_by": null,
    "invoice": null,
    "receipt": null,
    "tag": null,
    "tags": [],
    "payment_method": null,
    "payment_method_id": null,
    "payments": [],
    "comments": [],
    "extracted_data": null,
    "activities": [],
    "bank_transaction_match": null,
    "duplicates": [],
    "custom_fields": {}
}

Delete an expense

delete
/expense/{id}

Example response

json
// HTTP 204 No Content

List expenses

get
/expense

The list endpoint accepts the same parameters as in Retrieve an expense and returns a paginated array of the same expense object in the data property.

Read more about Pagination, Filtering, Sorting and Includes on the Introduction page.

Expense payments

For expense invoices (type invoice), you can manage outgoing payments to settle the expense using the Expense Payment endpoints. These payments track creditor information, bank details, and execution status for proper payment processing and reconciliation.