Skip to main content

Tour of the API

Bezala's data model has a small number of moving parts. Once you can name them and see how they connect, the API stops feeling like 40 unrelated resources and starts feeling like one consistent system.

J
Written by Julia Winberg

This article is the conceptual map. It doesn't go deep on any single endpoint — those have their own pages — but by the end of it, you'll know which endpoint to reach for in any given situation.

The big picture

Bezala turns spending events into accounting entries. There are four stages:

  1. A spending event happens. An employee buys lunch, drives somewhere for work, spends a day on a business trip, or earns a representational benefit.

  2. A user records it. As a receipt, a mileage trip, a daily allowance, or a reward. Either by hand in the app, by forwarding a receipt email, or via the API.

  3. An approver reviews it. Maybe one approver, maybe a chain of them. They can approve, send back for edits, or reject.

  4. The accounting batch sends it. Approved expenses get bundled into a batch and pushed to the customer's bookkeeping software, with SEPA payment files generated for whatever needs paying out.

The API exposes every stage. You can write into stage 1–2, observe stage 3, and trigger or read stage 4.

The four expense types

Bezala recognises four kinds of expense, each with its own endpoint family:

  • Receipts (/api/transactions) — anything backed by a receipt or invoice. Coffee, hotel rooms, software, equipment, vendor bills. The bread and butter of expense management.

  • Daily allowances (/api/daily_allowances) — per diems for business trips, calculated by country and trip duration with the appropriate Finnish, Swedish, or Norwegian tax rules. There's no receipt; the amount is set by law or company policy.

  • Trips (/api/trips) — mileage. Driven distance, vehicle, optionally additional passengers. Cost is calculated from distance and the per-kilometre rate.

  • Rewards (/api/rewards) — country-specific compensation that's neither a receipt nor mileage nor a per diem. Representational benefits, taxable employee perks, and similar. The shape mirrors the others.

Despite living in different endpoints, all four expense types share the same lifecycle (see below) and the same approval rules. If you can deal with receipts, you can deal with the others.

The expense lifecycle

Every expense moves through a state machine. The states are:

  • draft — an in-progress record. Not yet validated or sent to anyone. Useful for half-finished entries that need more information.

  • reviewing — a complete record waiting for an approver. The record is locked from edits by the submitter, but the approver can still update fields when approving.

  • unapproved — an approver rejected it with a comment. Goes back to the submitter to fix and resubmit.

  • queue — fully approved and waiting for the next accounting batch.

  • accounted — included in a batch that's been sent to bookkeeping. Locked. Read-only from this point on.

Every expense type has the same transitions; see the expense lifecycle for the diagram and the endpoints that move records between states.

Approvals

When an expense leaves draft and enters reviewing, Bezala figures out who needs to approve it. Three sources of approvers can apply, sometimes layered:

  1. The user's default approver — set on the user record.

  2. Account approvers — set on the expense account being charged. Can be a chain (A then B) or a branch (A or B).

  3. Cost-center approvers — set on the cost centers the expense touches.

  4. Transaction-specific approvers — set ad-hoc on a single record via POST /api/transactions/:id/approvers. Override the rest.

The result is a sequence of approvals that need to happen in order. Each approval is an authenticated PUT to the relevant /approve endpoint by the right user. See approval workflows.

Configuration: the pieces that hold expenses together

A receipt isn't just a number on a piece of paper — it's coded against the customer's chart of accounts, the right VAT, the right cost dimensions, and (sometimes) a vendor or a budget. Those structures are configured per company:

  • Accounts (/api/accounts). Two types: asset accounts (bank, cash, personal card, company card) and expense accounts (meals, travel, software, equipment). Every receipt has a credit account (asset) and a list of debit accounts with amounts (expense). See "the chart of accounts".

  • Cost centers (/api/cost_centers). Grouped into dimensions — Project, Office, Department, Cost Center. A company can define any number of dimensions, and an expense can carry up to one cost center per dimension.

  • Vendors (/api/vendors). The optional payee on a receipt. Used when the receipt should be paid to a third party rather than reimbursed to the employee.

  • VAT codes. Returned as part of /api/home. Each VAT line on a receipt picks one.

  • Budgets (/api/budgets). Pre-approved spending envelopes that link a user, expense accounts, optional cost centers, and a date range. Receipts can attach to a budget line via budget_line_id.

When a receipt enters Bezala via email forwarding rather than the API, transaction rules (/api/transaction_rules) and account rules (/api/account_rules) can pre-fill expense account, asset account, VAT, cost center, and user based on text in the email. They're how a customer makes "any receipt from Finnair lands as travel expense, billed to project X" happen automatically.

Credit cards and reconciliation

When employees use a corporate card, the cleanest workflow is:

  1. The card statement (or a real-time feed) flows into Bezala as a bill (/api/bills).

  2. Each line on the statement becomes a bill line (/api/bill_lines).

  3. Employees attach their receipts. Each bill line gets matched to a receipt: bill_line.receipt_id = receipt.id.

  4. Lines that genuinely don't need a receipt (recurring SaaS, subscriptions with the receipt elsewhere) are marked "no receipt needed".

The lines that are still unmatched at the end of the cycle become missing receipts — a list the employee sees on their dashboard. GET /api/missing_receipts returns the user's own outstanding ones.

Some card providers integrate directly: instead of waiting for the statement, they push transactions in real time via POST /api/:provider/bill_lines. The employee sees the transaction within minutes and can attach the receipt while it's still fresh.

See credit card bills, bills lines, and missing receipts for the full reconciliation flow.

Time and absence

Two parallel tracking systems sit alongside expenses:

  • Work time (/api/work_time_entries). Daily hours per user, with regular vs overtime distinctions. The standard time-and-attendance flow.

  • Project time (/api/time_entries). Hours per project per day. Used for billable time, with separate set_invoiced_at and billable calculation endpoints.

Both follow the same lifecycle as expenses (draft → reviewing → approved → reported_at). reported_at is the timestamp when the entry was sent to payroll; once set, the entry is locked.

Absences (/api/absences) are the third leg: vacation, sick leave, and country-specific long absences. Same lifecycle, plus a vacation-balance endpoint that tells you how many days a user has left.

Sending to accounting

The terminal stage. Approved expenses sit in the queue state until either:

  • A scheduled batch run fires (the company configures the schedule — daily, weekly, monthly).

  • An accountant or manager calls PUT /api/send_batch.

Either way, Bezala generates the bookkeeping voucher and SEPA payment file (where applicable), pushes them to the connected accounting system (Netvisor, Procountor, Fortnox, NetSuite, Visma Nova, etc.), and moves every included expense to accounted. The batch itself becomes a batch document (/api/batch_documents) with a reference number, a PDF summary, and links to the integration documents. From here, the expenses are read-only.

See sending expenses to accounting.

Putting it together: the journey of a single receipt

To anchor the model, here's what happens when an employee buys a €42.50 lunch with a personal card on a Tuesday:

  1. They forward the restaurant's receipt email to their personal Bezala address (receipts_email from /api/auth/token). Bezala creates a draft transaction with the receipt attached. Account rules pre-fill it as a "Meals" expense; transaction rules might also assign a project cost center.

  2. They open the app, fill in the missing fields, set the credit account to "Personal card", and submit. The transaction moves to reviewing.

  3. Their manager receives a notification, opens the transaction, and approves it. Because there are no chained approvers on this expense account, the transaction moves directly to queue.

  4. The next morning the scheduled batch fires. Bezala generates a Netvisor voucher debiting Meals and crediting "Reimbursable to employee", a SEPA payment file from the company's bank to the employee, a PDF summary, and creates a batch document containing this transaction (and any others queued at that moment). The transaction is now accounted and read-only.

If you're integrating, you can intervene at any point: create the draft via the API instead of email; pre-fill all the fields up front; trigger the approval programmatically; trigger the batch send programmatically; pull the batch document and bookkeeping voucher number back into your own system.

Now, on to the object pages — start with receipts.

Did this answer your question?