Understand Vexless

Companies

One per customer of yours. The company owns its books, its chart, its parties and its connections, and grants access to partners like you.

Create a company

POST /v1/companies
{ "name": "Asiakas Oy", "businessIdentifier": "1234567-8", "jurisdiction": "FI",
  "sandbox": true,                      # optional; enables the simulated bank and sandbox endpoints
  "fiscalYearStart": "2026-01-01" }     # optional; defaults to 1 January of the current year
{ "id": "cmp_…", "name": "Asiakas Oy", "businessIdentifier": "1234567-8", "jurisdiction": "FI", "currency": "EUR",
  "lockDate": null, "sandbox": true, "createdAt": "…",
  "connections": [ { "id": "con_…", "kind": "PARTNER_API", "provider": "…", "status": "ACTIVE", "consentUrl": null },
                   { "id": "con_…", "kind": "EINVOICE", … }, { "id": "con_…", "kind": "AUTHORITY", … },
                   { "id": "con_…", "kind": "BANK", "provider": "sandbox", "status": "ACTIVE", "consentUrl": null } ],
  "externalAccounts": [ { "id": "xac_…", "connectionId": "con_…", "kind": "BANK", "externalId": "FI0000000000000000", "accountNumber": "1910" } ],
  "fiscalYears": [ { "id": "fy_…", "startDate": "2026-01-01", "endDate": "2026-12-31", "closedAt": null, "closedBy": null, "resultAmount": null, "closingOriginId": null, "openingOriginId": null } ] }

The company arrives with the Finnish chart of accounts, an e-invoice and an authority connection, a calendar fiscal year, and the creating partner's grant with the standard scopes. Jurisdiction is FI only in this version; the book currency is EUR.

GET /v1/companies?cursor=&limit=       # every company your partner holds a grant on; summaries
GET /v1/companies/{companyId}          # with connections, externalAccounts, fiscalYears

The company object

id, name, businessIdentifierThe business identifier is the Y-tunnus
jurisdiction, currencyFI, EUR
sandboxbooleanSet at creation; never changes
lockDatedate | nullEntries on or before it are final. Moved by an accountant.
connections[{ id, kind, provider, status, consentUrl }]Kinds: PARTNER_API, BANK, EINVOICE, AUTHORITY, PAYMENT_PROCESSOR. Status: ACTIVE, NEEDS_AUTH, ERROR.
externalAccounts[{ id, connectionId, kind, externalId, accountNumber }]Bank accounts and the like, each mapped to a ledger account. externalId is the IBAN for a bank account.
fiscalYearsfiscalYear[]See below

Chart of accounts

Per company, copied from the jurisdiction pack at creation. Minimal in this version: it covers the flows in these docs, and an accountant with accounts:manage adds what a company needs.

GET /v1/companies/{companyId}/accounts
{ "items": [ { "id": "acc_…", "number": "1700", "name": "Myyntisaamiset", "type": "ASSET", "openItems": true, "status": "ACTIVE", "reportingCode": "SAAMISET", "createdAt": "…" },
             { "id": "acc_…", "number": "1910", "name": "Pankkitili", "type": "ASSET", "openItems": false, "status": "ACTIVE", "reportingCode": "RAHAT_JA_PANKKISAAMISET", … },
             { "id": "acc_…", "number": "2880", "name": "Ostovelat", "type": "LIABILITY", "openItems": true, "status": "ACTIVE", "reportingCode": "LYHYTAIKAINEN_VIERAS_PAAOMA", … }, … ],
  "next": null }
  • number is a string, so sub-accounts and leading zeros survive. Postings name accounts by number.
  • type: ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE.
  • openItems: postings here are cleared against each other and must carry a party. Receivables (1700), payables (2880), accruals.
  • reportingCode: the statutory report line the account rolls up to. The reports group by it.

The accounts the examples use: 1700 receivables, 2880 payables, 1910 bank, 1763 VAT receivable, 2939 VAT payable, 3010 service revenue, 6000 rent, 6900 bank fees.

POST /v1/companies/{companyId}/accounts        # accounts:manage
{ "number": "6510", "name": "Cloud services", "type": "EXPENSE", "openItems": false, "reportingCode": "LIIKETOIMINNAN_MUUT_KULUT" }

Tax codes

Shared per jurisdiction, not per company. The code is the identity — it is what you send on a posting and what you read back. Each names its rate, the VAT account a posting under it belongs on, and the VAT return line it is reported under. An interpretation tags postings with them.

GET /v1/tax-codes?jurisdiction=FI
{ "items": [ { "jurisdiction": "FI", "code": "FI_SALE_25_5", "name": "Myynti 25,5 %", "kind": "SALE", "rateBps": 2550, "validFrom": "2024-09-01", "validTo": null, "returnLine": "301", "vatAccountNumber": "2939" },
             { "code": "FI_PURCHASE_25_5", "kind": "PURCHASE", "rateBps": 2550, "returnLine": "307", "vatAccountNumber": "1763", … },
             { "code": "FI_SALE_EU_SERVICES", "kind": "ZERO", "rateBps": 0, "returnLine": "311", … },
             { "code": "FI_PURCHASE_EU_SERVICES", "kind": "EU_SERVICES", "rateBps": 2550, "returnLine": "313", … }, … ],
  "next": null }

Kinds: SALE, PURCHASE, REVERSE_CHARGE, EU_GOODS, EU_SERVICES, IMPORT, ZERO, EXEMPT. Codes carry validity dates; the 24 % codes ended on 2024-08-31.

Dimensions

A company's own reporting labels, created by anyone with origins:write. See Accounting & reporting.

Fiscal years and the lock

Fiscal years exist for entry numbering and year-end. The first is created with the company; later ones are created as calendar years when an entry first needs one. Entry numbers restart per fiscal year.

GET /v1/companies/{companyId}/fiscal-years
→ { "items": [ { "id": "fy_…", "startDate": "2026-01-01", "endDate": "2026-12-31", "closedAt": null, "closedBy": null, "resultAmount": null, "closingOriginId": null, "openingOriginId": null } ] }

The lock date is the company's lockDate. An accountant with close:manage moves it forward, usually monthly, and company.locked tells you. Entries dated on or before it cannot change; an origin dated inside the locked range posts on the first open date and keeps its document date. Closing a fiscal year books the result to equity, locks the year, and fires fiscal_year.closed with resultAmount, closingOriginId and openingOriginId on the year from then on.

Connections

Every way documents reach a company is a connection, so every origin has the same provenance. Your API access is the PARTNER_API connection. E-invoice and authority connections come with the company. A bank connection is started by you and completed by the customer:

POST /v1/companies/{companyId}/connections        # connections:manage
{ "kind": "BANK", "provider": "op" }
{ "id": "con_…", "status": "NEEDS_AUTH", "consentUrl": "https://…" }      # send your user there

Real bank and e-invoice providers are not connected in this version. A bank connection stays in NEEDS_AUTH; sandbox companies have an active simulated bank instead. See Banking.

Access

The company owns its data. Your partner acts on it through a grant, created with the company and extendable to other partners, an accounting office for instance. A company your partner holds no grant on answers 404 COMPANY_NOT_FOUND, as if it did not exist.