External API

Finpilo provides an external API for integrating other systems — accounting software, ERP platforms, or custom internal tools — with your workspace.

This page is a reference for developers or integration consultants building a connection between Finpilo and another system.

What you can do through the API

  • Sync partners (suppliers) into Finpilo from your accounting system.
  • Sync dimension values (cost centers, VAT codes, posting accounts) from your accounting system.
  • Read processed documents, including extracted data, line items, and assigned dimensions.
  • Download the original file of any document.
  • Trigger a send of a validated document to the configured webhook.

Documents are created in Finpilo only through the Portal UI or email forwarding — you cannot create documents through the API. Partner and dimension data, however, can flow in both directions.

Authentication

All API requests must include an X-API-Key header containing your workspace API key.

X-API-Key: fp_your_workspace_key

Generate the key in Workspace SettingsAPI tab. See Workspace Settings for instructions.

The key is scoped to a single workspace. All resource access is automatically limited to that workspace — you cannot access entities or documents outside it.

Reference IDs

Most endpoints use your own reference IDs (the string identifiers you set when creating entities, partners, or dimension values in Finpilo) rather than internal UUIDs. This makes integration simpler — use the same IDs your accounting system uses.

  • companyReferenceId — the entity's reference ID (e.g., ENT-00001).
  • referenceId on suppliers and dimension values — the identifier you chose.

Documents are the exception — they are addressed by their internal UUID, returned by the list endpoint.

Partners (Suppliers)

Endpoint prefix: /api/v1/companies/{companyReferenceId}/suppliers

Batch upsert partners

POST /api/v1/companies/{companyReferenceId}/suppliers

Creates new partners or updates existing ones. Match is by referenceId. Each item is processed independently — failures do not roll back other successful items.

Limits: 1 to 10,000 items per request.

Request body:

[
  {
    "referenceId": "SUP-001",
    "legalName": "Acme Supplies OÜ",
    "regNo": "12345678",
    "vatNo": "EE123456789",
    "legalAddress": "Tallinn, Estonia",
    "bankAccounts": ["EE382200221020145685"]
  }
]

Required fields: referenceId, legalName. All others are optional.

Response 200:

{
  "created": 2,
  "updated": 1,
  "failed": 0,
  "results": [
    { "referenceId": "SUP-001", "status": "created" },
    { "referenceId": "SUP-002", "status": "updated" },
    { "referenceId": "SUP-003", "status": "failed", "error": "..." }
  ]
}

List partners

GET /api/v1/companies/{companyReferenceId}/suppliers

Query parameters:

Parameter Type Default Description
page int 1 Page number
pageSize int 100 Items per page, max 1000
updatedSince ISO 8601 UTC Only return partners updated after this timestamp

Get single partner

GET /api/v1/companies/{companyReferenceId}/suppliers/{referenceId}

Response 200:

{
  "id": "uuid",
  "referenceId": "SUP-001",
  "legalName": "Acme Supplies OÜ",
  "regNo": "12345678",
  "vatNo": "EE123456789",
  "legalAddress": "Tallinn, Estonia",
  "bankAccounts": ["EE382200221020145685"],
  "createdAt": "ISO 8601",
  "updatedAt": "ISO 8601"
}

Dimension values

Endpoint prefix: /api/v1/companies/{companyReferenceId}/dimensions/{dimensionReferenceId}/items

dimensionReferenceId is the reference ID of the dimension as set in the entity's Dimensions tab.

Batch upsert dimension values

POST /api/v1/companies/{companyReferenceId}/dimensions/{dimensionReferenceId}/items

Same partial-success semantics as partners.

Limits: 1 to 10,000 items per request.

Request body:

[
  {
    "referenceId": "DIM-00001",
    "name": "Administration",
    "description": "Accounting, legal, management fees"
  }
]

Required fields: referenceId, name. description is optional.

List dimension values

GET /api/v1/companies/{companyReferenceId}/dimensions/{dimensionReferenceId}/items

Query parameters: page, pageSize (max 1000), updatedSince — same as partners.

Get single dimension value

GET /api/v1/companies/{companyReferenceId}/dimensions/{dimensionReferenceId}/items/{referenceId}

Response 200:

{
  "id": "uuid",
  "referenceId": "DIM-00001",
  "name": "Administration",
  "description": "Accounting, legal, management fees",
  "allocationListReferenceId": "DEPT",
  "createdAt": "ISO 8601",
  "updatedAt": "ISO 8601"
}

Documents

Endpoint prefix: /api/v1/companies/{companyReferenceId}/documents

Documents are read-only through the API. They are created only through upload or email in the Finpilo portal.

List documents

GET /api/v1/companies/{companyReferenceId}/documents

Query parameters:

Parameter Type Default Description
page int 1 Page number
pageSize int 50 Items per page, max 500
status string Filter by status (e.g., validated, sent)
documentType string Filter by document type name
issuedFrom ISO 8601 Issue date range start
issuedTo ISO 8601 Issue date range end
sentOnly bool If true, only documents already sent via webhook

Response 200: array of document payload objects (see schema below).

Get single document

GET /api/v1/companies/{companyReferenceId}/documents/{documentId}

Returns the full document including all line items and dimension allocations.

Download original file

GET /api/v1/companies/{companyReferenceId}/documents/{documentId}/file

Returns the original uploaded file as a binary stream. Content-Type matches the original upload (PDF, JPEG, PNG).

Finpilo does not convert or re-encode files — you receive exactly what was uploaded.

Send document to webhook

POST /api/v1/companies/{companyReferenceId}/documents/{documentId}/send

Triggers the send flow — Finpilo posts the document to the configured webhook (entity webhook if set, workspace webhook otherwise).

Query parameters:

Parameter Type Default Description
resend bool false If true, allows re-sending a document that was already sent

Response 200:

{
  "success": true,
  "accountingReferenceId": "INV-2026-0042"
}

accountingReferenceId is the value returned by your webhook endpoint (if any).

Response 400 — send failed:

{ "error": "Custom webhook is enabled for this entity but no URL is configured." }

Document payload schema

This is the structure returned by the document endpoints and also sent as the webhook body when a document is sent.

{
  "documentId": "3fa0b1c2-d3e4-5f67-8901-abcdef123456",
  "documentType": "Invoice",
  "direction": "Incoming",
  "documentNumber": "INV-2025-0042",
  "issueDate": "2025-01-15T00:00:00Z",
  "dueDate": "2025-02-15T00:00:00Z",
  "servicePeriodStartDate": "2025-01-01T00:00:00Z",
  "servicePeriodEndDate": "2025-01-31T00:00:00Z",
  "currency": "EUR",

  "supplierName": "Acme Supplies OÜ",
  "supplierRegNumber": "12345678",
  "supplierVatNumber": "EE123456789",
  "supplierAddress": "Tallinn, Estonia",
  "supplierBankAccounts": ["EE382200221020145685"],
  "supplierReferenceId": "SUP-001",

  "recipientName": "My Company SIA",
  "recipientRegNumber": "40003012345",
  "recipientVatNumber": "LV40003012345",
  "recipientAddress": "Riga, Latvia",
  "recipientBankAccounts": null,
  "recipientReferenceId": "ENT-00001",

  "lineItemsSubtotalExcludingTax": 1000.00,
  "allowancesAmountExcludingTax": 0,
  "chargesAmountExcludingTax": 0,
  "retentionAmountExcludingTax": 0,
  "totalExcludingTax": 1000.00,
  "taxAmount": 200.00,
  "totalIncludingTax": 1200.00,
  "prepaidAmountIncludingTax": null,
  "payableAmountIncludingTax": 1200.00,

  "comment": null,
  "validatedAt": "2025-01-20T09:15:00Z",
  "entityName": "My Company SIA",
  "fileUrl": "https://docmanager-api.../api/v1/companies/ENT-00001/documents/3fa0b1c2-.../file",

  "lineItems": [
    {
      "position": 1,
      "title": "Consulting services",
      "description": "January 2025",
      "supplierCode": "CONS-001",
      "unit": "h",
      "quantity": 10.0,
      "unitPriceExcludingTax": 100.00,
      "totalExcludingTax": 1000.00,
      "taxRate": 20.0,
      "taxAmount": 200.00,
      "totalIncludingTax": 1200.00,
      "allocations": [
        {
          "allocationListReferenceId": "DEPT",
          "dimensionName": "Department",
          "code": "IT",
          "name": "IT Department",
          "description": null
        }
      ]
    }
  ],
  "allocations": []
}

Fields with no data are null (never omitted). lineItems and allocations are always arrays (empty [] if none).

Webhook integration flow

When Finpilo sends a document to your webhook, follow this typical flow:

  1. Receive the HTTP POST. The body is application/json matching the payload schema above.
  2. If you configured an API key in the Finpilo webhook settings, it arrives in the Authorization: Bearer <key> header. Verify it before processing.
  3. Create a record in your system using the structured data.
  4. Fetch the original file from fileUrl using your Finpilo workspace API key (the same one used for all API calls).
  5. Attach the file to your record.
  6. Return a JSON response with accountingReferenceId set to your internal record ID. An empty 200 OK response is also valid if you do not track the reference.

Finpilo waits up to 30 seconds for your response. Requests that exceed this are marked as failed.

Headers sent by Finpilo

Every webhook request includes:

Content-Type: application/json
Authorization: Bearer <api-key>

The Authorization header is only included if you configured an API key in the webhook settings.

Error responses

Status Meaning
401 Unauthorized Missing or invalid X-API-Key
404 Not Found Reference ID not found within the workspace
400 Bad Request Validation failure (empty batch, batch > 10,000 items, send errors)

All error responses use the shape { "error": "description" }.