menu

Introduction

This document describes the public REST API of Zenvoices.

– OpenAPI specification: https://app.zenvoices.com/swagger/public-v1/swagger.json
– Swagger UI (interactive documentation): https://app.zenvoices.com/swagger/index.html?urls.primaryName=Zenvoices%20public%20API%20v1
– The API base URL is https://app.zenvoices.com/public-api/v1/.
– Only application/json content is supported.

Refer to chapter Available APIs for an overview of supported featrues.

Getting started

Register for API access

If you don’t have access to a Zenvoices user/tenant yet, start your free trial here.

Grant API access to a user

The Zenvoices public API uses access tokens for authentication. An access token can be retrieved by sending user login credentials to POST /api/TokenAuth/TokenLogin. API requests are executed with the permissions of the user used for authentication. Users don’t have API access by default, therefore we first need to grant API access to a user of your tenant.

Go to Manage / Organisation / Users in the Zenvoices portal. Create a new user which is only used for API access (recommended) or use an existing user. Edit the permissions of the user and grant the API permissions you’d like to use.

Note: users with only API access are not billed.

Get an access token

Call POST /api/TokenAuth/TokenLogin with your user credentials to get an access token. Access tokens are valid for 1 day and are used to authenticate API requests.

Example cURL command:

curl -X 'POST' \
  'https://app.zenvoices.com/api/TokenAuth/TokenLogin' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json-patch+json' \
  -d '{
  "userNameOrEmailAddress": "your-api-user-username",
  "password": "your-api-user-password",
  "tenancyName": "your-tenant-name",
}'

Note that tenancy name is the organisation code of your Zenvoices organisation.

Example response:

{
  "result": {
    "accessToken": "access-token",
    "expireInSeconds": 86400,
    "loginVerificationCode": null,
    "requiresTwoFactorVerification": false,
    "twoFactorAuthProviders": null,
    "refreshToken": "refresh-token",
    "refreshTokenExpireInSeconds": 31536000
  },
  "targetUrl": null,
  "success": true,
  "error": null,
  "unAuthorizedRequest": false,
  "__abp": true
}

In the response above the result.accessToken property contains the access token. In subsequent requests you must include this access token in the Authorization header with value “Bearer <access-token>”.

Once the access token has expired, you can get a new access token by calling the TokenLogin endpoint again or you can use the POST ​/api​/TokenAuth​/RefreshToken endpoint to retrieve a new token using the provided refresh token.

Make your first API call

For example call POST /public-api/v1/air/processDocument using the following cURL command:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'Authorization: Bearer <your-access-token>' -d '{ \ 
   "url": "https://your-domain/document.pdf", \ 
   "fileName": "document.pdf", \ 
   "legislation": "nl",   \ 
 }' 'https://app.zenvoices.com/public-api/v1/air/processDocument'

Go to our Swagger UI to discover all available operations and parameters.

Authentication

API calls are authenticated by providing an Authorization header using the Bearer scheme. An access token and refresh token can be retrieved by passing user login credentials to POST /api/TokenAuth/TokenLogin or passing a refresh token to POST ​/api​/TokenAuth​/RefreshToken. API requests are executed with the permissions of the user used for authentication. We recommend using a separate user for API access.

Example Authorization header:

Authorization: Bearer <your-access-token>

Example cURL request to retrieve an access token:

curl -X 'POST' \
  'https://app.zenvoices.com/api/TokenAuth/TokenLogin' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json-patch+json' \
  -d '{
  "userNameOrEmailAddress": "your-api-user-username",
  "password": "your-api-user-password",
  "tenancyName": "your-tenant-name",
}'

Example of an authenticated cURL request with an access token:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'Authorization: Bearer <your-access-token>' -d '{ \ 
   "url": "https://your-domain/document.pdf", \ 
   "fileName": "document.pdf", \ 
   "legislation": "nl",   \ 
 }' 'https://app.zenvoices.com/public-api/v1/air/processDocument'

Paging and sorting data

List endpoints support paging and sorting data.

Use the maxResultCount and skipCount input parameters to receive data in pages. The maximum result count per page is 250.

Use the sorting input parameter to sort data. The value of this parameter must be in the format: “[fieldname] [asc/desc], [fieldname] [asc/desc], …”. Sorting on all response fields is supported. If no sorting parameter value is provided, results will be sorted on the primary key.

Error handling

Error kind HTTP response code Description
Invalid input 400 This error is returned when invalid input is provided. error.validationErrors of the response body contains a list of invalid fields with related errors.
Unauthorized 401, 403 This error is returned when you’re not authorized to access the request resource.
Entity not found 404 This error is returned when a requested entity doesn’t exist.
Too many requests 429 This error is returned when the rate limit of an endpoint is reached.
General error 500 This error is returned when an error occured during request processing. Refer to error.message in the response body for details about the error.

Error messages are returned in the language that is configured for the authenticated user. You can override the user language by including the Abp.Localization.CultureName header in requests. Valid values are: nl (Dutch) and en (English).

Webhooks

The Zenvoices public API supports webhooks, which are “user-defined HTTP callbacks”. They are triggered by an event. When that event occurs, the source site makes an HTTP request to the URL configured for the webhook. Users can configure them to cause events on one site to invoke behavior on another. It’s a publish/subscribe system.

Webhook subscriptions can be created for all companies or for specific companies.

Payload format

Webhook calls have JSON payload with the following data structure:

  • Id (string): unique id of the webhook call
  • WebhookEvent (string): event name (see supported events section)
  • Attempt (int): attempt number
  • Data (object): event specific payload (see supported events section)
  • CreationTimeUtc (string): creation time of the webhook attempt in ISO 8601 format

Example:

{
"Id": "82479677-bed5-4543-82e1-4043854b3e7a",
"WebhookEvent": "App.FinancialTransaction.Exported",
"Attempt": 1,
"Data": {
"tenantId": 3332,
"administrationId": 12213,
"financialTransactionId": 1305229
},
"CreationTimeUtc": "2023-03-29T09:54:47.8028248Z"
}

Supported events

Name Description Example JSON payload
App.FinancialTransaction.Created Triggers when a financial transaction has been successfully created. {
tenantId: 1234,
administrationId: 1234,
financialTransactionId: 1234
}
App.FinancialTransaction.Exported Triggers when a financial transaction has been successfully exported. {
tenantId: 1234,
administrationId: 1234,
financialTransactionId: 1234
}

Managing webhook subscriptions

Webhook subscriptions can be managed in the Zenvoices portal using Manage / Organisation / Webhook subscriptions (permission must be granted) or using the webhook subscriptions endpoints of our public API.

Security

Every webhook notification contains an HMAC SHA256 hash signature. It creates a signature by hashing the HTTP request’s body. Your application which subscribed to a webhook should hash the received body and check whether it is equal with received one, to ensure it has not been modified by others. For hashing the secret key is used that you configured for the webhook subscription.

Retry mechanism and auto subscription deactivation

If your webhook URL is (temporarily) unavailable, we will retry the notification 5 times with an exponential backoff strategy.

When fifteen consecutive attempts fail, the webhook subscription will be deactivated. You can reactivate the webhook subscription manually in this case.

Limits and quotas

Max. requests per second per endpoint* 10
Max. items per page 250

* HTTP response code 429 is returned when the request limit is reached.

Data privacy and security

– Zenvoices is governed by strict standards regarding the privacy and protection of customer data. We take strong measures to protect your data from unauthorized persons or improper access.
– Zenvoices is ISO 27001:2017 and 9001:2015 certified and uses ISO 27001 certified infrastructure for hosting and data storage.
– More information about data privacy can be found in our privacy statement and data processing agreement.
– More information about security can be found here.

Client libraries

Currently no pre-built client libraries are available, but you can easily create your own client by leveraging our OpenAPI 2.0 specification, which is available on https://app.zenvoices.com/swagger/index.html?urls.primaryName=Zenvoices%20public%20API%20v1. For example, you can use Swagger Codegen to generate client libraries for languages such as C#, Java, PHP, Python, Perl, etc.

Swagger UI

Swagger UI is available on https://app.zenvoices.com/swagger/index.html?urls.primaryName=Zenvoices%20public%20API%20v1.

If you’re logged in to the Zenvoices portal (https://app.zenvoices.com), try out requests are authenticated as the user you’re currently logged in as.

If you’d like to authenticate try out requests as another user, login as this user in the Zenvoices portal or ensure you’re logged out in the Zenvoices portal (!) and click the login button in the Swagger UI header. Note the latter only works when you’re not already logged in to the Zenvoices portal.

Available APIs

Accounting systems

This API allows you to list available accounting system connectors.

Administrations

This API is used to list administrations (companies) and get administration details. Only administrations that are granted for the authenticated user are accessible.

Automatic invoice recognition (AIR)

This API allows the use of Zenvoices automatic invoice recognition (AIR) machine learning technology to extract key-value pairs and line items from invoice and receipt documents. It outputs structured data with details of the extracted values. You can train your own machine learning models to improve accuracy by providing feedback on recognition results.

Supported fields

Header fields
Field name Data type Extracts single or multiple values? Has confidence? Supports training? Description
InvoiceNumber String Single Yes Yes Invoice number.
InvoiceDate Date Single Yes Yes Date the invoice was issued.
DueDate Date Single Yes Yes Date invoice payment is due.
Currency String Single Yes Yes ISO 4217 currency code.
TotalAmountInclTax Number Single Yes Yes Total amount including VAT (TotalAmountExclTax + sum of tax line amounts)
TotalAmountExclTax Number Single Yes Yes Total amount excluding VAT
TaxLine String Multiple Yes (if not calculated) Yes Tax amount, tax rate and base amount separated by a semicolon (;). Up to three tax line fields can be extracted. Only tax lines with rates of the given legislation are extracted.
InvoiceCocNumber String Single Yes Yes Chamber of commerce number.
InvoiceCustomerIdentifier String Single Yes Yes Customer identifier.
AccountEmail String Single No No E-mail address.
InvoiceIBAN String Multiple Yes Yes IBAN (bank account number).
AccountPostalCode String Multiple No No Postal code.
InvoiceVatNumber String Multiple Yes Yes VAT number.
YourReference String Multiple Yes Yes Your reference numbers (i.e. reference numbers of the invoice recipient)
StructuredPaymentReference String Single Yes Yes Structured payment reference. Supported formats: ISO 11649 Creditor Reference, Belgian OGM (gestructureerde medeling).
TaxReverseChargedKeyword Boolean Single No No Bool indicating if tax is reverse charged.
InvoiceKeyword Boolean Single No No Bool indicating if the document is an invoice. If false, the document is a receipt.
CreditKeyword Boolean Single No No Bool indicating if the document is a credit note. If True and extracted amounts are positive, you should change their sign to make them negative.
YourOrderNumber String Multiple Yes Yes Purchase order number of the invoice recipient.
ContractNumber String Multiple Yes Yes Contract number.
EAN String Multiple Yes No European Article Numbers.
BelgianCompanyNumber String Multiple Yes No Belgian Company Number (ondernemingsnummer).
AddressName String Multiple Yes Yes Addressee (Name of recipient / company name)
AddressStreetName String Multiple Yes Yes Street Name
AddressStreetNumber String Multiple Yes Yes Street Number / House Number
AddressCityName String Multiple Yes Yes City Name
AddressCountryName String Multiple Yes Yes Country Name
AddressPostbox String Multiple Yes Yes Postbox
Addresses

Addresses are extracted like all other fields, but we attempt to group them with the GroupId property. GroupId is an arbitrary nullable int; extracted values with a non null GroupId should be grouped with other extracted values with the same GroupId. This indicates, for example, that a given Postal Code belongs to the same address as a given City Name.

Address extraction is currently in beta, and can be activated on request.

Line items

Line items are only extracted if the extractLineItems property is set to True in the POST/public-api/v1/air/processDocument call.

For each line item a confidence score is provided.

Field name Data type Description
Description String Description of the line.
ProductCode String Product or item code.
Quantity Number Quantity of the line.
Price Number Price of the line.
Amount Number Amount of the line (quantity * price).
TaxAmount Number Tax amount of the line.
TaxRate Number Tax rate of the line (e.g. 0.21 means 21 %)
ReferenceNumber String Reference numbers of the line.
OrderNumber String Purchase order number of the line.
LedgerAccountCode String Ledger account code of the line.

Excluding values from extraction

Using the “excludedValues” parameter of POST /public-api/v1/air/processDocument you can specify which field values should be excluded from extraction. For instance, if you’d like to extract data from purchase invoices you can exclude known customer account identifiers (such as VAT number and IBAN) to allow AIR to extract supplier account identifiers.

Custom models

Overview

You can train custom machine learning models to improve recognition accuracy. Custom models can be created using the endpoint POST /public-api/v1/air/model/createOrUpdate.

In the request body of the POST /public-api/v1/air/processDocument endpoint you can specify which model must be used for recognition using the modelId parameter. Our AI engine combines our pretrained models with your custom model to ensure optimal recognition accuracy on all features, even if the training data amount is small.

With the endpoint POST /public-api/v1/air/validateOperationResult you can provide feedback on process document operation results. This feedback is used to generate training data for your model.

After validating operation results, use the endpoint POST /public-api/v1/air/model/train to train your model. Approximately five to fifteen documents need to be validated with the validateOperationResult endpoint before training is possible. This differs by field and is currently necessary to avoid overfitting the custom model too much.

Providing feedback

By providing feedback on process document operation results you can improve recognition accuracy and increase confidence values.

Use the POST /public-api/v1/air/validateOperationResult endpoint to provide feedback on a operation. You can only provide feedback on operations that used a custom model.

Example request body:

{
  "operationId": "37bc8616-ce2f-40e6-ad72-896dcfa7787e",
  "correctValues": [
    {
      "fieldName": "InvoiceNumber",
      "correctValue": "20150122"
    },
   {
      "fieldName": "InvoiceDate",
      "correctValue": "2019-01-02T00:00:00.0000000"
    },
   {
      "fieldName": "TotalAmountInclTax",
      "correctValue": "121.00"
    },
   {
      "fieldName": "TotalAmountExclTax",
      "correctValue": "100"
    },
    {
      "fieldName": "TaxLine1",
      "correctValue": "21.00;0.21;100.00"
    },
    {
      "fieldName": "YourReference",
      "correctValue": "reference1;reference2;reference3"
    },
    {
      "fieldName": "AddressName",
      "correctValue": "SenderName;ReceiverName",
      "correctValueGroup": "1;2"
    },
    {
      "fieldName": "AddressStreetName",
      "correctValue": "SenderStreetName;ReceiverStreetName",
      "correctValueGroup": "1;2"
    },
    {
      "fieldName": "AddressStreetNumber",
      "correctValue": "SenderStreetNumber;ReceiverStreetNumber",
      "correctValueGroup": "1;2"
    },
    {
      "fieldName": "AccountPostalCode",
      "correctValue": "SenderPostalCode;ReceiverPostalCode",
      "correctValueGroup": "1;2"
    },
    {
      "fieldName": "AddressCityName",
      "correctValue": "SenderCityName;ReceiverCityName",
      "correctValueGroup": "1;2"
  ], 
  "correctTables": [ 
    { 
      "tableInfo": 
        { "tableX0": 0.1, "tableX1": 0.9, "tableY0": 0.4, "tableY1": 0.8, "pageNumber": 1, "isContinuation": false }, 
      "tableHeaders": [ 
        { "tableHeaderName": "HeaderDescription" }, 
        { "tableHeaderName": "NoPrediction", }, 
        { "tableHeaderName": "HeaderQuantity" }, 
        { "tableHeaderName": "HeaderBasePrice", }, 
        { "tableHeaderName": "HeaderTaxRate", }, 
        { "tableHeaderName": "HeaderTaxAmount", }, 
        { "tableHeaderName": "HeaderTotalPrice", }, 
      ] 
    }
  ]
}

Notes:

We recommend providing as many validated (correct) values as possible.

– Not providing correct values:
a) Not providing a correct value of a non amount field means the predicted value is incorrect, unless there is no predicted value.
b) Not providing a correct value of an amount field has no effect.

– If provided amounts are not found in the document, validation has no effect.

– Field names are case sensitive and may only occur once in the CorrectValues collection.

– CorrectValue values are formatted as strings using the following formats:
a) Date: ISO 8601 format, e.g. “2019-01-02T00:00:00.0000000”
b) Number: “.” is used as decimal separator, no thousand separator
c) TaxLine’s: “{tax amount};{tax rate};{base amount}”, e.g. “21.00;0.21;100.00”
d) Multiple extraction fields: use a semicolon separated string, for example “correct value1; correct value2”

-After providing feedback, a model must be retrained to let the model take advantage of the new training data.

Line item recognition

Line item recognition feedback can be provided using the CorrectTables property. Correct tables requires a “tableInfo” field and a “tableHeaders” field. Currently it is possible to fine tune the table bounds detection (location of the table in the page), and the table header classification.

Notes:

– Table bounds should be normalized, from 0 to 1.
– pageNumber is indexed from 1.
– isContinuation = true means this table is a continuation of the previous table in the list of correctTables.
– Table headers (the list of supported table headers is below) are defined from left to right. Use the NoPrediction header name for a column that is not in the supported list. It is currently not possible to train column types for tables without headers.

Please refer to our Swagger documentation for more details.

The following table header names are supported:

– HeaderTotalPrice
– HeaderTaxAmount
– HeaderTaxRate
– HeaderBasePrice
– HeaderQuantity
– HeaderProductCode
– HeaderDescription
– HeaderOrderNumber
– HeaderReferenceNumber
– HeaderLedgerAccount
– NoPrediction

Training a model

You can train a model using POST /public-api/v1/air/model/train. Training is performed asynchronously and the training time depends on the amount of training data. A minimum of ~25 validated operation results is required before training is possible.

Using POST /public-api/v1/air/model/list (and checking the fields lastTrainingTime and isTrainingScheduled) you can verify if training is finished and has succeeded.

Data type value formatting

Extracted values are encoded as strings using the formats described in the table below.

Data type Format Example value
Date ISO 8601 format “2019-01-02T00:00:00.0000000”
Number “.” is used as decimal separator, not as thousands separator “12345.12”
Boolean “True” or “False” “True”

Limits and quotas

Supported document types .pdf, .docx, .doc, .xlsx, .xls, .eml, .jpg, .jpeg, .png, .bmp
Supported legislations* nl, gb, de, be, pl, fr, es, usa
Supported document cultures nl, en, es, fr, de, pl, cs (Czech)
Max. document file size 25 MB (after base64 encoding)
Max. document dimensions 10.000 x 10.000 pixels
Max. document pages No limit, but at most 50 pages are processed
Operation result lifetime 48 hours
Max. number of excluded feature values 50
Max. number of ML models 50**
Minimum time between model training operations 2 * average training time from the last 10 training operations

* Legislations are used for tax amount extraction only.

** Support can increase this limit on request.

Data storage

– Raw input documents are deleted when a process document operation finishes.
– If no custom model is used, operation results are deleted ~48 hours after the operation was finished.
– If a custom model is used, operation results and training data are stored until manual deletion of the model.

Billing

– The AIR API is available in all Zenvoices subscriptions (including Lite).
– One transaction is charged per five pages, with a maximum of ten transactions per document.
– Purchasing transaction bundles allows you to save up to 46% on transaction costs.
– See our terms of use and pricing page for transaction pricing and more information.

Financial transactions

This API allows you to list financial transactions and get financial transaction details.

Inbox

This API allows you to upload documents to the inbox of an administration.

Purchase orders

This API allows you to create, edit, delete and list purchase orders of an administration. To use this API the purchase order master data source of the administration must be configured to API.

Products

This API allows you to create, edit, delete and list products of an administration. To use this API the products master data source of the administration must be configured to API.

Users

This API allows you to list users and get user details including permissions.

Webhooks

This API allows you to manage webhook subscriptions.

Support & SLA

Changelog

02-09-2024: added authorization endpoints and the “App.FinancialTransaction.Created” event.

03-08-2024: changed IBAN and VAT number fields into multiple extraction fields, added Czech language support in AIR, added EAN number field in AIR.

12-06-2024: added identified languages to AIR API output.

01-05-2024:

Added raw tables to the AIR API output.

Added extracted word bounding boxes to the AIR API output.

Added AIRResult to GET/public-api/v1/financialTransaction/{financialTransactionId} output.

Added financial transaction line commitmentExternalId property to GET/public-api/v1/financialTransaction/{financialTransactionId} output.

21-03-2024: extended POST/public-api/v1/financialTransaction/list with the properties UsedProposalStrategy, LineLedgerAccountCodes, LineCostCentreCodes, LineCostUnitCodes, LineProjectCodes, LineProductCodes, LineEmployeeCodes, AccountMatchType.

14-03-2024: added IsProposalChanged to POST/public-api/v1/financialTransaction/list and GET/public-api/v1/financialTransaction/{financialTransactionId}.

06-10-2023: added YourOrderNumber and ContractNumber fields to the AIR API.

16-08-2023: added procuratorStatusChanges to the response of GET /public-api/v1/financialTransaction/{financialTransactionId}

19-05-2023: extended error handling section with localization information.

27-12-2022: added products and purchase orders endpoints.

13-12-2022: added webhooks.

29-7-2022: changed authentication docs to recommend usage of POST /api/TokenAuth/TokenLogin instead of POST /api/Account/Authenticate (which is obsolete).

23-5-2022: increased AIR API max document file size from 10 MB to 25 MB, remove image limit of 4 MB and increased max. document dimension size from 3200 x 3200 to 10.000 x 10.000 pixels.

19-3-2022: changed rate limit from max. 10 request per second to max. 10 requests per second per endpoint.

14-12-2021: fixed inbox documents attachment not being visible after uploading an inbox document.

02-09-2021: added the ability to sort lists by specifying the new Sorting parameter.

09-07-2021: changed AreAllAdministrationsGranted field value from user to global level (i.e. also considering role permissions) in endpoint GET /public-api/v1/user/{userId}.

30-06-2021: fixed incorrect values of GrantedAdministrationIds field in endpoint GET /public-api/v1/user/{userId}.

11-06-2021:

Added users endpoints

Added accounting systems endpoints

Added downloadUrl property to FinancialTransactionAttachmentDto (GET /public-api/v1/financialTransaction/{financialTransactionId})

12-05-2021:

Added totalCount property to the response of endpoints that support paging.

Added enabledFeatures property to the respone of endpoint GET /public-api/v1/administration/{administrationId}

Fixed administration filter issue of endpoint POST /public-api/v1/administration/list

04-05-2021: changed response date formatting of AIR end points to ISO 8601

29-04-2021: initial version