NAV
Shell

Introduction

Welcome to the TheNextInvoice API documentation.

Base URL

Currently, the API lives under the domain https://api.thenextinvoice.com/api/v1/. For brevity, all paths in this documentation will elide this base URL.

Response envelope

Response envelope

{
  "success": true,
  "data": {},
  "error": {
    "code": 0,
    "message": "error message"
  }
}

Our responses will always be wrapped in an envelope.

Of this envelope, some properties will be elided based on the type of response. As such, the error object will only exist when success is false.

For the brevity of the documentation, this envelope will henceforth be elided.

Authentication

General day-to-day authentication is done either through a JWT token, or an API key.

JWTs are accepted through the Authorization header.

example: Authorization: Bearer {token}.

API tokens are accepted through the X-Api-Token header.

example: X-Api-Token: {token}.

Login using JWT

No authentication needed for this request.

POST /session/jwt

The way of logging in a user directly, using email and password. Can optionally specify what company to log into.

Example curl request

curl -X POST \
  -H 'Content-Type: application/json' \
  -d '{ "email": "user@example.com", "password": "pa$$word" }' \
  https://api.thenextinvoice.com/api/v1/session/jwt

Example request

{
    "email": "user@example.com",
    "password": "pa$$word",
    "company": 1
}

Parameters

Parameter In Type Required Description
email body string Yes Email address
password body string Yes Password
company body integer No Company ID to log in with

Example response

{
    "token": "jwt.auth.token",
    "status": "confirmed"
}

Responses

Status Description
200 Login successful
401 The given combination of email, password (and optionally company) is not found.

Switching company

POST /session/jwt/switch

After logging in, you can freely switch between the companies the user has access to. See Listing Companies for details on getting a list of companies.

Example request

{
    "company": 1
}

Parameters

Parameter In Type Required Description
company body integer Yes Company ID to request a token for

Example response

{
    "token": "jwt.auth.token"
}

Responses

Status Description
200 Request successful
400 Invalid request
401 You do not have access to the given company, or it does not exist

User preferences

GET /preferences

Returns various preferences related to the current company.

Example response

{
    "package": "premium",
    "addons": [
        "lightspeed",
        "payt"
    ],
    "language": "EN",
    "confirmed": true,
    "grace": 0,
    "user_data": {
        "name": "TNI beta user",
        "email": "tni@example.com"
    },
    "company_name": "TNI Beta"
}

Update user preferences

PUT /preferences

Updates the current user’s preferences. Returns the full preferences object (the same shape as GET /preferences).

Parameters

Parameter In Type Required Description
language body string Yes Preferred UI language, e.g. EN

Example request

{
    "language": "EN"
}

Responses

Status Description
200 Preferences updated
500 Update failed

Ledger

Listing ledgers

GET /settings/ledgernumbers

Retrieves a list of all ledger numbers for the current company.

Example response

[
  {
    "id": 76,
    "name": "Revenue",
    "ledgernumber": "8000",
    "costcenter": ""
  },
  {
    "id": 85,
    "name": "Management fee",
    "ledgernumber": "4010",
    "costcenter": ""
  },
  {
   "id": 76,
   "name": "Shipping",
   "ledgernumber": "4020",
   "costcenter": "KPL0001"
  }
]

Responses

Status Description
200 Request successful

Creating ledger numbers

POST /settings/ledgernumbers

Creates a new ledger number

Parameters

Parameter In Type Required Description
name body string Yes Human readable name for the new ledger number
ledgernumber body string No Code used for bookkeeping software. e.g. 4000, 8010
costcenter body string No Cost center code used for bookkeeping software, e.g. KPL0001, KP2

Example response

{
  "id": 155,
  "name": "Revenue",
  "ledgernumber": "8000",
  "costcenter": ""
}

Responses

Status Description
200 Request successful
400 Request failed

Updating ledger numbers

PUT /settings/ledgernumbers/{id}

Updates a ledger number

Parameters

Parameter In Type Required Description
id query int Yes Ledger id
name body string Yes Human readable name for the new ledger number
ledgernumber body string No Code used for bookkeeping software. e.g. 4000, 8010
costcenter body string No Cost center code used for bookkeeping software, e.g. KPL0001, KP2

Example response

{
  "id": 155,
  "name": "Revenue",
  "ledgernumber": "8000",
  "costcenter": ""
}

Responses

Status Description
200 Request successful
404 Ledger not found
400 Request failed

Deleting ledger numbers

DELETE /settings/ledgernumbers

Removed a ledger number

Parameters

Parameter In Type Required Description
id query int Yes Ledger id

Example response

"Ledgernumber has been removed"

Responses

Status Description
200 Request successful
404 Ledger not found
500 Request failed

Invoice

Listing Invoices

GET /invoice/search

Retrieves a paginated list of all invoices in the current company.

Example query

GET /invoice/search?limit=10&reverse=true&status=&type=invoice&order=number&page=1

Parameters

Parameter In Type Required Default Description
order query string No number / id Field to order by. Options: id, customer, number, date, duedate, collection_date, amount. Default depends on type (number for invoice/quotation, id for draft/recurring/sepa).
reverse query boolean No false Reverses the order of the results (true/false)
limit query integer No 20 Return max limit rows in the paginated response. Max 100.
page query integer No 1 What page of the pagination to return
status query string No Filter by status, comma-separated (max 5). Options: open, paid, pastdue, credit, credited, reminded, booked, notbooked, sepa
type query string No invoice Invoice type. Options: invoice, quotation, recurring, draft, sepa
term query string No Free-text search term
customer_id query integer No Filter by customer id
minnumber query string No Minimum invoice number
maxnumber query string No Maximum invoice number
mindate query string No Minimum invoice date
maxdate query string No Maximum invoice date
minduedate query string No Minimum due date
maxduedate query string No Maximum due date
minamount query string No Minimum total amount of the invoice
maxamount query string No Maximum total amount of the invoice

Example response

{
  "current": 1,
  "first": 1,
  "previous": 1,
  "next": 1,
  "last": 1,
  "limit": 20,
  "total_items": 1,
  "items": [
    {
      "id": 45311,
      "company": 1,
      "number": "202400013",
      "status": 1,
      "type": "",
      "discount": 0,
      "includesVat": false,
      "lines": [
        {
          "description": "Product 1",
          "ledger": 11,
          "price": 100,
          "quantity": 1,
          "vat": 3
        }
      ],
      "sender": 1,
      "receiver": 2,
      "meta": {
        "currency": "EUR",
        "dueDate": "2024-05-27",
        "language": "nl",
        "sendDate": "2024-03-28"
      },
      "text": {
        "top": "This invoice is for item 1425",
        "bottom": "Thank you for your order.",
        "footer": ""
      },
      "calculated": {
        "totalExclVat": 100,
        "totalInclVat": 121,
        "discountApplied": 0,
        "vatApplied": 21,
        "vatTotals": {
          "21": 21
        }
      }
    }
  ]
}

Responses

Status Description
200 Request successful

Status of an invoice

GET /invoice/{id}/status

Retrieves the status details of an invoice

Example query

GET /invoice/12552/status

Example response

{
    "invoice": {
        "state": "paid"
    },
    "booking": {
        "status": true,
        "method": "twinfield",
        "message": "Invoice 201800013 has been booked",
        "time": "2018-04-12 18:30:45"
    },
    "mail": {
          "status": true,
          "events": [
            {
              "recipient": "debtors@example.com",
              "event": "queued",
              "time": "2019-02-28 21:30:00"
            },
            {
              "recipient": "debtors@example.com",
              "event": "delivered",
              "time": "2019-02-28 20:30:01"
            }
          ]
        },
    "comments": {
        "status": true,
        "comments": [
            {
                "user": {
                    "id": 1,
                    "name": "TNI beta user"
                },
                "message": "This is a nice comment",
                "time": "2023-12-20 11:13:37"
            }
        ]
    },
    "sepa": {
        "status": false,
        "message": null,
        "time": null
    },
    "collection": {
        "status": false,
        "date": null,
        "type": null
    },
   "payment": {
         "status": true,
         "message": "Bankoverschrijving",
         "time": "2019-01-21 00:00:00"
   }, 
    "reminder": {
        "status": false,
        "count": "0",
        "time": null
    },
    "recurring": {
        "status": false,
        "time": null
    },
    "quotation": {
        "status": false,
        "message": null,
        "time": null
    }
}

Responses

Status Description
200 Invoice found
404 Invoice not found

Create Invoice

POST /invoice

Create a new invoice.

Parameters

Parameter In Type Required Default Description
creationToken body string No Optional: a client-side generated idempotency UUID
discount body float Yes 0 Discount in percentage
includesVat body boolean Yes false Amounts are including or excluding VAT
lines body object Yes List of invoice lines
meta body object Yes Invoice meta object
receiver body int or object Yes Receiver id or full Receiver object
sender body int or object Yes Sender id or full Sender object
status body int No 0 Status of the invoice: 0 for drafts, 1 for finalized
text body object Yes Invoice text object
type body string No "" The type, either regular (empty string), C for Credit or I for Automatic Collection

Invoice Line object

Parameter Type Required Description
quantity float Yes Item quantity. Can be null if ledger, price, vat are all null
description string Yes Description, can span multiple lines
ledger int or object Yes Ledger id or Ledger object. Can be null if quantity, price, vat are all null
vat int or object Yes Vat id or Vat object. Can be null if quantity, ledger, price are all null
price float Yes Item price. Can be null if quantity, ledger, vat are all null

Example request

{
    "discount": 0,
    "includesVat": false,
    "lines": [
        {
            "description": "Product 1",
            "ledger": 11,
            "price": 100,
            "quantity": 1,
            "vat": 3
        },
        {
            "description": "Description-only line",
            "ledger": null,
            "price": null,
            "quantity": null,
            "vat": null
        }
    ],
    "meta": {
        "currency": "EUR",
        "dueDate": "2019-05-27",
        "language": "nl",
        "sendDate": "2019-03-28"
    },
    "receiver": 2,
    "sender": 1,
    "status": 0,
    "text": {
        "bottom": "Thank you for your order.",
        "footer": "Wij verzoeken u het factuurbedrag ter hoogte van {factuurbedrag} voor {vervaldatum} te voldoen op onze bankrekening onder vermelding van het factuurnummer {factuurnummer}. Alvast bedankt!",
        "top": "This invoice is for item 1425"
    },
    "type": ""
}

Example response

45311

Responses

Status Description
200 Invoice created
400 Invoice not valid
500 Invoice saving failed

Update an Invoice

PUT /invoice/{id}

Update a draft invoice. This endpoint expects a full Invoice object, as described under Create an Invoice.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id
Invoice object body json Yes Invoice data

Example query

PUT /invoice/44783

Example response

44892

Responses

Status Description
200 Invoice updated
400 Invoice not valid
500 Invoice saving failed, or invoice was finalized

Convert a draft Invoice into finalized

POST /invoice/{id}/send

Finalize a draft invoice.

Example request

POST /invoice/45311/send

Example response

45311

Responses

Status Description
200 Invoice created
400 Invoice not valid
500 Invoice saving failed

Credit an invoice

POST /invoice/{id}/credit/

Credit an invoice marking the current invoice as ‘credited’ and creating and new invoice. In the newly created invoice, that is automatically marked as a finalized ‘credit’, all amounts are converted to the opposite signs.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id

Example request

POST /invoice/44783/credit

Example response

44789

Responses

Status Description
201 Invoice credited, new one created
400 Invoice not valid or invalid state transition
404 Invoice to be credited not found
500 Invoice saving failed

Get the invoice payment url

GET /invoice/{id}/payment-url

Get the payment url for an invoice. When a user follows this link, they reach a landing page where they can view the invoice pdf, and pay using one of the payment providers set up by the sending company.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id

Example request

GET /invoice/44783/payment-url

Example response

thenextinvoice.com/t/a1b2c3d4e5f6

Responses

Status Description
200 Request successful
404 Invoice not found

Register an invoice payment

POST /invoice/{id}/payment

Register a finalized invoice as paid using the provided method and transaction date.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id
method body int Yes Payment method id
date body string Yes Date on which the invoice was paid

Example request

{
    "date": "2019-04-16", 
    "method": 8
}

Responses

Status Description
201 Invoice marked as paid
400 Invoice or request not valid
404 Invoice or payment method not found
409 Invoice already paid
500 Payment saving failed

Add a comment

POST /invoice/{id}/comment

Invoices support multiple comments, using this endpoint one at a time can be added.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id
message body string Yes The comment you want to add

Example request

{
    "message": "This is a nice comment"
}

Example response

{
    "id": 123,
    "invoice_id": 44783,
    "user_id": 1,
    "time": "2023-12-20 11:13:37",
    "message": "This is a nice comment"
}

Responses

Status Description
200 Request successful
400 Comment not valid or required field missing
404 Invoice not found
500 Comment saving failed

Get PDF for Invoice

GET /invoice/{id}/view

Download the PDF of a given invoice.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id

Example query

GET /invoice/44783/view

Example response

binary: application/pdf;

Responses

Status Description
200 Request successful

Email Invoice

POST /invoice/{id}/send/mail

Email a single, finalized invoice. All placeholders are available and will be replaced with the values corresponding to the invoice.

When it is desired to send multiple invoices, use the /invoice/queue/ endpoint below.

Parameters

Parameter In Type Required Default Description
id query int Yes Invoice id
subject body string Yes Email subject
body body string Yes Email body
attachments body object No Optional attachments, with base64 encoded binary in content field

Example request

{
    "subject": "Invoice {invoicenumber}",
    "body": "<p>Dear {companyname},</p><p><br></p><p>Thank you for your order. Attached please find invoice {invoicenumber} that is due on {duedate}.</p><p>You can pay this invoice directly by following the link: {paymentlink}</p><p><br></p><p>Sincerely,</p><p><br></p><p>{companyname}</p>",
    "attachments": {
          "content": "base64 encoded binary",
          "filename": "Order confirmation.png"
    }
}

Example response

45311

Responses

Status Description
200 Invoice mailed
400 Invoice not valid or required field missing
404 Invoice not found
500 Invoice saving failed

Bulk operations

Several invoice actions accept a comma-separated list of ids in place of a single {id}, e.g. POST /invoice/45311,45312,45313/book. These are noted as “bulk” below.

Delete invoice(s)

DELETE /invoice/{id}

Soft-delete one or more invoices. Bulk (comma-separated ids).

Parameters

Parameter In Type Required Description
id path string Yes Invoice id, or comma-separated ids

Responses

Status Description
200 Invoice(s) deleted
404 Invoice not found

Book invoice(s)

POST /invoice/{id}/book

Queue one or more invoices to be booked into the connected accounting system. Bulk.

Responses

Status Description
200 Updated the booking queue
404 Invoice not found
500 Could not add invoice to queue

Remind invoice(s)

POST /invoice/{id}/remind

Register a payment reminder for one or more invoices. Bulk. Takes no body.

Responses

Status Description
200 Reminder(s) created
500 Could not save reminder

Approve invoice(s)

POST /invoice/{id}/approve

Approve one or more quotations. Bulk. Takes no body.

Responses

Status Description
200 Invoice(s) approved
400 Invalid state

Archive invoice(s)

POST /invoice/{id}/archive

Archive one or more quotations. Bulk. Takes no body.

Responses

Status Description
200 Invoice(s) archived
400 Invalid state

Queue multiple invoices for mailing

POST /invoice/{id}/queue

Queue finalized invoices to be emailed. Bulk.

Parameters

Parameter In Type Required Description
id path string Yes Invoice id(s)
subject body string Yes Email subject
body body string Yes Email body

Responses

Status Description
200 Invoices added to queue
400 Missing subject and/or body

Create SEPA batch

POST /invoice/{id}/sepa

Create a SEPA direct-debit batch for one or more invoices. Bulk.

Parameters

Parameter In Type Required Description
id path string Yes Invoice id(s)
date body string Yes Collection date, YYYY-MM-DD

Responses

Status Description
200 SEPA batch created
400 Date empty or invalid
404 Invoice not found

Send invoice by SMS

POST /invoice/{id}/send/sms

Send a finalized invoice to its receiver by SMS. Requires SMS to be enabled and the receiver to have a phone number. The SMS text is generated automatically.

Responses

Status Description
200 Sent
403 SMS not enabled
400 Receiver has no phone number
404 Invoice not found

Send invoice over Peppol

POST /invoice/{id}/send/peppol

Send a finalized invoice over the Peppol network. Requires a Peppol-verified sender profile.

Responses

Status Description
200 Sent
403 Sender profile not Peppol-verified
404 Invoice not found

Resend an invoice

POST /invoice/{id}/resend

Resend a finalized invoice through its original send channel. Takes no body.

Responses

Status Description
200 Resent
404 Invoice not found

Set tracking number

POST /invoice/{id}/tracking-number

Set the shipment tracking number on an invoice.

Parameters

Parameter In Type Required Description
id path int Yes Invoice id
tracking_number body string Yes Shipment tracking number

Responses

Status Description
200 Tracking number saved
404 Invoice not found
500 Failed to save tracking number

Delete invoice payment

DELETE /invoice/{id}/payment

Remove the registered payment from a paid invoice.

Responses

Status Description
200 Payment removed
409 Invoice is not paid
404 Invoice not found

Get UBL for invoice

GET /invoice/{id}/ubl

Get the UBL (XML) document for a finalized invoice.

Parameters

Parameter In Type Required Default Description
id path int Yes Invoice id
peppol query boolean No false Render the Peppol BIS variant of UBL

Responses

Status Description
200 UBL document (application/xml)
400 Cannot generate UBL from a draft
404 Invoice not found

Sync payments

POST /invoice/syncpayments

Trigger a reconciliation sync of invoice payments from connected payment providers. Takes no body.

Responses

Status Description
200 Invoice payments are being synced
500 Could not request payment sync

Get an invoice

GET /invoice/{id}

Retrieves a single invoice by id, with the full invoice object (the same shape as the items returned by invoice search).

Draft invoices (status: 0) return receiver, sender and calculated as null. These are resolved and snapshotted onto the invoice only when it is finalized, so totals and embedded sender/receiver details are not yet available for drafts. To find drafts for a specific customer, filter invoice search by customer_id (the link exists even though receiver is not yet populated).

Parameters

Parameter In Type Required Description
id path int Yes Invoice id

Responses

Status Description
200 Invoice found
404 Invoice not found

Invoice totals

GET /invoice/totals

Returns aggregate totals for the invoices matching the given filters. Accepts the same filter parameters as invoice search (type, status, term, customer_id, date/amount ranges). Totals are keyed by currency.

Example response

{
  "EUR": {
    "amount_incl": 12100.00,
    "amount_excl": 10000.00
  }
}

Responses

Status Description
200 Request successful
404 No totals found for this period

Invoice downloads

These endpoints return binary/file responses rather than JSON.

Download invoice PDF

POST /invoice/{id}/send/pdf

Generates and returns the invoice PDF.

Example response

binary: application/pdf

Responses

Status Description
200 PDF returned
404 Invoice not found
500 Could not generate PDF

Download invoice ZIP

GET /invoice/{id}/zip

Returns a ZIP containing the invoice PDF and any attachments. Bulk (comma-separated ids).

Example response

binary: application/zip

Responses

Status Description
200 ZIP returned
404 Invoice not found

Download an invoice attachment

GET /invoice/{id}/attachment/{attachmentId}

Returns a single attachment file for the invoice, with its original content type.

Parameters

Parameter In Type Required Description
id path int Yes Invoice id
attachmentId path int Yes Attachment id

Responses

Status Description
200 Attachment returned
404 Invoice/attachment not found

Download packing slip

GET /invoice/{id}/packing-slip

Returns the packing-slip PDF for an invoice.

Example response

binary: application/pdf

Responses

Status Description
200 PDF returned
404 Invoice not found

Export invoices as CSV

GET /invoice/csv

Exports the invoices matching the current filters as a ;-separated CSV. Accepts the same filter parameters as invoice search.

Example response

binary: text/csv

Responses

Status Description
200 CSV returned

Customer

Listing Customers

GET /customer

Retrieves a list of all customers in the current company.

Parameters

Parameter In Type Required Description
term query string No Search term for name or contact

Example response

[
    {
        "id": "1",
        "debnumber": "debtor number",
        "companyname": "Company name",
        "companycontact": "Contact name",
        "address": "Address Street 101",
        "postalcode": "1111AA",
        "city": "Amsterdam",
        "country": "NL",
        "vatnumber": "VAT NUMBER",
        "email": "company@example.com",
        "ccaddress": "companycc@example.com",
        "phone": "+31699999999",
        "iban": "NL73EXMPL000000000",
        "optionalfield_1": null,
        "optionalfield_2": null,
        "person": "0",
        "option_expiration": null,
        "option_extratext": null,
        "option_extratextbottom": null,
        "option_language": null,
        "option_currency": null
    },
    {
        "id": "2",
        "debnumber": null,
        "companyname": "Example Person",
        "companycontact": null,
        "address": "Somewhere Street 1",
        "postalcode": "111111",
        "city": "Amsterdammeke",
        "country": "NL",
        "vatnumber": "",
        "email": "person@example.com",
        "ccaddress": "",
        "phone": null,
        "iban": null,
        "optionalfield_1": null,
        "optionalfield_2": null,
        "person": "1",
        "option_expiration": null,
        "option_extratext": null,
        "option_extratextbottom": null,
        "option_language": null,
        "option_currency": null
    }
]

Responses

Status Description
200 Request succesful

Paginated Customers

GET /customer/search

Retrieves a paginated list of all customers in the current company.

Example query

GET /customer/search?limit=10&page=1&term=Test

Parameters

Parameter In Type Required Default Description
term query string No Search term for name or contact
limit query integer No 50 Return max limit rows in the paginated response. Max 100.
page query integer No 1 What page of the pagination to return

Example response

{
    "current": "1",
    "first": "1",
    "items": [
        {
            "id": "1",
            "debnumber": "debtor number",
            "companyname": "Company name",
            "companycontact": "Contact name",
            "address": "Address Street 101",
            "postalcode": "1111AA",
            "city": "Amsterdam",
            "country": "NL",
            "vatnumber": "VAT NUMBER",
            "email": "company@example.com",
            "ccaddress": "companycc@example.com",
            "phone": "+31699999999",
            "iban": "NL73EXMPL000000000",
            "optionalfield_1": null,
            "optionalfield_2": null,
            "person": "0",
            "option_expiration": null,
            "option_extratext": null,
            "option_extratextbottom": null,
            "option_language": null,
            "option_currency": null
        },
        {
            "id": "2",
            "debnumber": null,
            "companyname": "Example Person",
            "companycontact": null,
            "address": "Somewhere Street 1",
            "postalcode": "111111",
            "city": "Amsterdammeke",
            "country": "NL",
            "vatnumber": "",
            "email": "person@example.com",
            "ccaddress": "",
            "phone": null,
            "iban": null,
            "optionalfield_1": null,
            "optionalfield_2": null,
            "person": "1",
            "option_expiration": null,
            "option_extratext": null,
            "option_extratextbottom": null,
            "option_language": null,
            "option_currency": null
        }
    ],
    "last": "1",
    "limit": "50",
    "next": "1",
    "previous": "1",
    "total_items": "2"
}

Responses

Status Description
200 Request successful

Get Single Customer

GET /customer/{id}

Get all details for a single customer.

Parameters

Parameter In Type Required Description
id path string Yes ID of customer

Example response

{
    "id": "1",
    "debnumber": "debtor number",
    "companyname": "Company name",
    "companycontact": "Contact name",
    "address": "Address Street 101",
    "postalcode": "1111AA",
    "city": "Amsterdam",
    "country": "NL",
    "vatnumber": "VAT NUMBER",
    "email": "company@example.com",
    "ccaddress": "companycc@example.com",
    "phone": "+31699999999",
    "iban": "NL73EXMPL000000000",
    "optionalfield_1": null,
    "optionalfield_2": null,
    "person": "0",
    "option_expiration": null,
    "option_extratext": null,
    "option_extratextbottom": null,
    "option_language": null,
    "option_currency": null
}

Responses

Status Description
200 Request succesful
404 Customer does not exist

Create Customer

POST /customer

Create a new customer.

Example request

{
    "companyname": "Company name",
    "companycontact": "Contact name",
    "address": "Address Street 101",
    "postalcode": "1111AA",
    "city": "Amsterdam",
    "country": "NL",
    "email": "company@example.com",
    "person": false
}

Parameters

Parameter In Type Required Description
companyname body string Yes Customer name
companycontact body string No Contact name (if customer is not a person)
address body string Yes Address
postalcode body string Yes Postal code
city body string Yes City
country body string Yes Country code
email body string Yes Email address
ccaddress body string No CC email address
debnumber body string No Debtor number
vatnumber body string No VAT number
phone body string No Phone number
iban body string No IBAN number
optionalfield_1 body string No optional field
optionalfield_2 body string No optional field
person body bool Yes is customer a person (true) or company (false)
option_expiration body number No custom expiration time for invoices
option_extratext body string No custom extra text for invoices
option_extratextbottom body string No custom extra text (bottom) for invoices
option_language body string No custom language for invoices
option_currency body string No custom currency for invoices

Example response

{
    "id": 1
}

Responses

Status Description
200 Request succesful
400 Invalid request

Update Customer

PUT /customer/{id}

Updates an existing customer. All properties that are not set will be defaulted back to their default value.

Example request

{
    "companyname": "Company name",
    "companycontact": "Contact name",
    "address": "Address Street 101",
    "postalcode": "1111AA",
    "city": "Amsterdam",
    "country": "NL",
    "email": "company@example.com",
    "person": false
}

Parameters

Parameter In Type Required Description
companyname body string Yes Customer name
companycontact body string No Contact name (if customer is not a person)
address body string Yes Address
postalcode body string Yes Postal code
city body string Yes City
country body string Yes Country code
email body string Yes Email address
ccaddress body string No CC email address
debnumber body string No Debtor number
vatnumber body string No VAT number
phone body string No Phone number
iban body string No IBAN number
optionalfield_1 body string No optional field
optionalfield_2 body string No optional field
person body bool Yes is customer a person (true) or company (false)
option_expiration body number No custom expiration time for invoices
option_extratext body string No custom extra text for invoices
option_extratextbottom body string No custom extra text (bottom) for invoices
option_language body string No custom language for invoices
option_currency body string No custom currency for invoices

Example response

{
    "id": 1
}

Responses

Status Description
200 Request succesful
400 Invalid request

Delete Customer

DELETE /customer/{id}

Deletes an existing customer.

Responses

Status Description
200 Customer deleted
404 Customer does not exist

Resolving customer data through VAT number

GET /customer/checkvat

Attempts to resolve the given VAT information to a company, verifying if it exists, and if so returning some basic properties.

Example query

GET /customer/checkvat?countryCode=NL&vatNumber=857494454B01

Parameters

Parameter In Type Required Description
countryCode query string Yes Country code for the VAT number
vatNumber query string Yes VAT number (without country code)

Example response

{
    "name": "Thenextinvoice b.v.",
    "street": "Condensatorweg 00054",
    "zip": "1014AX",
    "city": "Amsterdam"
}

Responses

Status Description
200 Request succesful
400 Invalid request
404 VAT code not found

Resolving customer data through Chamber of Commerce

GET /customer/checkcoc

Looks up a Dutch company in the Chamber of Commerce (KvK) register by CoC number or by name. Provide at least one of number or name.

Example query

GET /customer/checkcoc?number=12345678

Parameters

Parameter In Type Required Description
number query string No* Chamber of Commerce number
name query string No* Company name to search for

* At least one of number or name is required.

Example response

{
    "name": "Thenextinvoice b.v.",
    "street": "Condensatorweg 54",
    "zip": "1014AX",
    "city": "Amsterdam"
}

Responses

Status Description
200 Request succesful
400 No name or number given
404 Company not found

Listing duplicate customers

GET /customer/duplicates

Returns groups of customers that appear to be duplicates (matched on name / contact details), to support de-duplication.

Responses

Status Description
200 Request succesful

Merging customers

PUT /customer/merge/{id}

Merges one or more duplicate customers into the master customer {id}: all invoices belonging to the listed customers are reassigned to the master, and the merged customers are removed.

Parameters

Parameter In Type Required Description
id path int Yes Master customer id to merge into
(body) body array Yes JSON array of customer ids to merge into the master

Example request

[
    12,
    34,
    56
]

Responses

Status Description
200 Customers merged
404 Customer does not exist

Importing customers

POST /customer/import

Bulk-creates customers from a JSON array of customer objects (each object uses the same fields as Create Customer). The import is transactional: if any row fails validation the whole import is rolled back.

Example request

[
    {
        "companyname": "Company A",
        "address": "Street 1",
        "postalcode": "1111AA",
        "city": "Amsterdam",
        "country": "NL",
        "email": "a@example.com",
        "person": false
    },
    {
        "companyname": "Company B",
        "address": "Street 2",
        "postalcode": "2222BB",
        "city": "Rotterdam",
        "country": "NL",
        "email": "b@example.com",
        "person": false
    }
]

Responses

Status Description
200 Customers imported
400 A row failed validation (rolled back)

Verifying a customer for Peppol

GET /customer/verifypeppol/{id}

Checks whether a customer is a registered participant on the Peppol network. Requires Peppol to be enabled for the company.

Parameters

Parameter In Type Required Description
id path int Yes Customer id

Responses

Status Description
200 Verification result returned
403 Peppol not enabled
404 Customer does not exist

Product

Listing Products

GET /product/search

Lists (and optionally searches for) a list of all products in the current company. Response is paginated.

Example query

GET /product/search?order=id&page=1

Parameters

Parameter In Type Required Default Description
term query string No Search term for product description
order query string No id Specifies which field to order by. Options are id, description, price
reverse query boolean No false Reverses the order of the results
limit query integer No 10 Return max limit rows in the paginated response. Max 100.
page query integer No 0 What page of the pagination to return

Example response

{
    "before": 1,
    "current": 1,
    "first": 1,
    "items": [
        {
            "description": "Product 1",
            "id": 1,
            "ledgernumber_id": 1,
            "price": 101,
            "vat_id": 3
        },
        {
            "description": "Product 2",
            "id": 7,
            "ledgernumber_id": null,
            "price": 10,
            "vat_id": null
        },
        {
            "description": "Product 3",
            "id": 9,
            "ledgernumber_id": 11,
            "price": 12,
            "vat_id": 2
        },
        {
            "description": "Product 4",
            "id": 10,
            "ledgernumber_id": 11,
            "price": 15,
            "vat_id": 3
        },
        {
            "description": "Product 5",
            "id": 13,
            "ledgernumber_id": 11,
            "price": 10,
            "vat_id": 1
        }
    ],
    "last": 140,
    "limit": 5,
    "next": 2,
    "total_items": 699,
    "total_pages": 140
}

Responses

Status Description
200 Request succesful

Get Single Product

GET /product/{id}

Get all details for a single product.

Parameters

Parameter In Type Required Description
id path string Yes ID of product

Example response

{
    "description": "Product 2",
    "id": 7,
    "ledgernumber_id": null,
    "price": 10,
    "vat_id": null
}

Responses

Status Description
200 Request succesful
404 Product does not exist

Create Product

POST /product

Create a new product.

Example request

{
  "description": "Description",
  "price": 100,
  "vat_id": null,
  "ledgernumber_id": null
}

Parameters

Parameter In Type Required Description
description body string Yes Product description
price body number Yes Price of the product
vat_id body number No ID of the associated VAT number
ledgernumber_id body number No ID of the associated ledger number

Example response

"Product has been created"

Responses

Status Description
200 Request succesful
400 Invalid request

Update Product

PUT /product/{id}

Updates a product.

Example request

{
  "description": "Updated Description",
  "price": 1000,
  "vat_id": 4,
  "ledgernumber_id": null
}

Parameters

Parameter In Type Required Description
id path number Yes ID of the product to update
description body string Yes Product description
price body number Yes Price of the product
vat_id body number No ID of the associated VAT number
ledgernumber_id body number No ID of the associated ledger number

Example response

{
  "id": 1,
  "description": "Updated Description",
  "price": 1000,
  "vat_id": 4,
  "ledgernumber_id": null
}

Responses

Status Description
200 Request succesful
400 Invalid request

Delete Product

DELETE /product/{id}

Deletes an existing product.

Parameters

Parameter In Type Required Description
id path number Yes ID of the product to delete

Example response

"Product has been removed"

Responses

Status Description
200 Product deleted
404 Product does not exist

Company

Listing Companies

GET /company

Retrieves a list of all companies the current user has access to.

NOTE: See Profile Logo for usage of the logo option.

Parameters

Parameter In Type Required Description

Example response

[
  {
    "id": 1,
    "name": "Company name",
    "address": "Company address",
    "postalcode": "Company zipcode",
    "city": "Amsterdam",
    "coc": "CoC code",
    "next_number": "000001",
    "next_qnumber": "000001",
    "country": "NL",
    "logo": "1.png"
  }
]

Responses

Status Description
200 Request succesful
401 You do not have access to any companies

VAT codes

Table of types:

Type Description
high High
low Low
exempt Exempt
none No VAT
ICPS Services
ICPG Goods
EBU Export outside EU

Listing VAT codes

GET /settings/vatcodes

Retrieves a list of all vat codes for the current company.

Example response

[
  {
    "id": 155,
    "name": "6% BTW",
    "code": "VL",
    "factor": 1.06,
    "type": null
  },
  {
    "id": 156,
    "name": "21% BTW",
    "code": "VH",
    "factor": 1.21,
    "type": null
  },
  {
    "id": 154,
    "name": "Geen BTW",
    "code": "",
    "factor": 1,
    "type": "none"
  }
]

Responses

Status Description
200 Request successful

Creating VAT codes

POST /settings/vatcodes

Creates a new VAT code

Parameters

Parameter In Type Required Description
name body string Yes Human readable name for the new VAT code
code body string No Code used for bookkeeping software. e.g. VL, VH
factor body number Yes Multiplier factor for the VAT. e.g. 1.21 => 21% VAT
type body string Yes Type of VAT code. See VAT codes for a table of options

Example response

{
    "id": 155,
    "name": "6% BTW",
    "code": "VL",
    "factor": 1.06,
    "type": null
}

Responses

Status Description
200 Request successful
400 Request failed

Updating VAT codes

PUT /settings/vatcodes/{id}

Updates a VAT code

Parameters

Parameter In Type Required Description
id query int Yes Vat id
name body string Yes Human readable name for the new VAT code
code body string No Code used for bookkeeping software. e.g. VL, VH
factor body number Yes Multiplier factor for the VAT. e.g. 1.21 => 21% VAT
type body string No Type of VAT code. See VAT codes for a table of options

Example response

{
    "id": 155,
    "name": "6% BTW",
    "code": "VL",
    "factor": 1.06,
    "type": null
}

Responses

Status Description
200 Request successful
400 Request failed
404 Vat not found

Deleting VAT codes

DELETE /settings/vatcodes/{id}

Remove a VAT code

Parameters

Parameter In Type Required Description
id query int Yes VAT id

Example response

"Vatcode has been removed"

Responses

Status Description
200 Request successful
404 VAT code not found
500 Request failed

Listing VAT codes (percentage form)

GET /vatcodes

Like GET /settings/vatcodes, but returns the VAT rate as a percentage rather than a factor. For example a 21% code is returned as factor: 21 here, versus factor: 1.21 from /settings/vatcodes. Useful when you want the rate to display directly.

Example response

[
  {
    "id": 156,
    "name": "21% BTW",
    "code": "VH",
    "factor": 21,
    "type": null
  },
  {
    "id": 154,
    "name": "Geen BTW",
    "code": "",
    "factor": 0,
    "type": "none"
  }
]

Responses

Status Description
200 Request successful

Analytics

Turnover

GET /analytics/turnover/{interval}

Returns turnover for the current company over the last 12 periods. The response contains 12 labels and 12 corresponding values, oldest first.

Parameters

Parameter In Type Required Description
interval path string Yes Aggregation: month or year

Example query

GET /analytics/turnover/month

Example response

{
  "labels": ["Jul 2025", "Aug 2025", "Sep 2025", "Oct 2025", "Nov 2025", "Dec 2025", "Jan 2026", "Feb 2026", "Mar 2026", "Apr 2026", "May 2026", "Jun 2026"],
  "values": [1200.5, 980, 0, 3400.75, 1500, 2100, 1800, 950, 2750, 3010.4, 1990, 2230]
}

Responses

Status Description
200 Request successful
400 Invalid interval (allowed: month, year)

Top customers

GET /analytics/customers/{limit}

Returns the top customers by turnover (within the last year), highest first.

Parameters

Parameter In Type Required Description
limit path integer Yes Number of customers to return (1–50)

Example response

[
  { "customerid": 2, "companyname": "ACME B.V.", "amount": 14250.00 },
  { "customerid": 9, "companyname": "Example Person", "amount": 8800.50 }
]

Responses

Status Description
200 Request successful
400 Invalid limit (must be 1–50)

Prognosis

A prognosis holds a company’s financial forecast. costs, debts and revenue are JSON arrays of monthly figures, starting from start_month/start_year.

Get prognosis

GET /prognosis

Returns the current company’s prognosis, or null if none has been set.

Example response

{
  "id": 12,
  "company_id": 1,
  "start_month": 1,
  "start_year": 2026,
  "costs": [1000, 1000, 1200],
  "debts": [0, 500, 0],
  "revenue": [3000, 3200, 4100]
}

Responses

Status Description
200 Request successful

Save prognosis

POST /prognosis or PUT /prognosis

Creates or updates the company’s prognosis (a company has at most one).

Parameters

Parameter In Type Required Description
start_month body integer Yes First month of the forecast (1–12)
start_year body integer Yes First year of the forecast
costs body array Yes Monthly cost figures
debts body array Yes Monthly debt figures
revenue body array Yes Monthly revenue figures

Example request

{
  "start_month": 1,
  "start_year": 2026,
  "costs": [1000, 1000, 1200],
  "debts": [0, 500, 0],
  "revenue": [3000, 3200, 4100]
}

Responses

Status Description
200 Prognosis saved
400 Invalid request

SEPA

SEPA direct-debit batches are created from invoices (see Create SEPA batch under Invoice). The endpoints here list and download existing batches.

Listing SEPA batches

GET /sepa/search

Retrieves a paginated list of SEPA batches for the current company. Accepts the same pagination and filter parameters as invoice search (limit, page, order, reverse, date/amount ranges, …).

Example query

GET /sepa/search?limit=10&page=1

Responses

Status Description
200 Request successful

SEPA totals

GET /sepa/totals

Returns aggregate totals for the SEPA batches matching the current filters, keyed by currency.

Example response

{
  "EUR": {
    "amount_incl": 4235.00,
    "amount_excl": 3500.00
  }
}

Responses

Status Description
200 Request successful

Download a SEPA file

GET /sepa/{id}/view

Returns the SEPA direct-debit XML (pain.008) for a batch, ready to upload to a bank.

Parameters

Parameter In Type Required Description
id path int Yes SEPA batch id

Example response

binary: application/xml

Responses

Status Description
200 Request successful
404 SEPA batch not found

Payment methods

Listing payment methods

GET /settings/paymentmethods

Retrieves a list of all payment methods for the current company.

Example response

[
  {
    "id": 8,
    "name": "Bank transfer"
  },
  {
    "id": 76,
    "name": "Stripe"
  }
]

Responses

Status Description
200 Request successful
404 No payment methods found

Creating payment methods

POST /settings/paymentmethods

Creates a new payment method

Parameters

Parameter In Type Required Description
name body string Yes Human readable name for the new payment method

Example response

{
  "id": 155,
  "name": "Mollie iDeal transfer"
}

Responses

Status Description
200 Request successful
400 Request failed

Updating payment methods

PUT /settings/paymentmethods/{id}

Updates a payment method

Parameters

Parameter In Type Required Description
id query int Yes Payment method id
name body string Yes Human readable name for the payment method

Example response

{
  "id": 155,
  "name": "iDeal transfer"
}

Responses

Status Description
200 Request successful
400 Request failed
404 Payment method not found

Deleting payment methods

DELETE /settings/paymentmethods

Remove a payment method from this company

Parameters

Parameter In Type Required Description
id query int Yes Payment method id

Example response

"Payment method has been removed"

Responses

Status Description
200 Request successful
404 Payment method not found
500 Request failed

Company settings

General per-company configuration. (VAT codes, ledger numbers and payment methods have their own pages.)

Invoice & quotation numbering

GET /settings/numbers

Returns the next invoice and quotation numbers.

Example response

{
  "invoice": "000001",
  "quotation": "00001"
}

PUT /settings/numbers

Updates the next invoice and/or quotation numbers.

Parameters

Parameter In Type Required Description
invoice body string No Next invoice number
quotation body string No Next quotation number

Responses

Status Description
200 Success (GET) / numbers updated (PUT)
400 Update failed (PUT)
404 Company not found

Debit ledger number

GET /settings/ledgernumberdebit

Returns the debit (accounts-receivable) ledger number used for bookkeeping (defaults to 1000).

Example response

{ "ledgerNumber": 1300 }

PUT /settings/ledgernumberdebit

Parameters

Parameter In Type Required Description
ledgerNumber body integer Yes Debit ledger number

Responses

Status Description
200 Request successful
400 ledgerNumber empty (PUT)
500 Save failed (PUT)

SEPA creditor settings

GET /settings/sepa

Returns the company’s SEPA creditor configuration (used when creating SEPA direct-debit batches).

Example response

{
  "id": 1,
  "company_id": 1,
  "creditor_name": "ACME B.V.",
  "iban": "NL73EXMPL000000000",
  "creditor_identifier": "NL98ZZZ999999990000"
}

PUT /settings/sepa

Updates the creditor settings. Values are validated; validation is skipped only if all three are empty.

Parameters

Parameter In Type Required Description
creditor_name body string No Creditor name (max 70 chars)
iban body string No Creditor IBAN
creditor_identifier body string No SEPA creditor identifier

Responses

Status Description
200 SEPA information updated
400 Invalid name / IBAN / identifier

Payment messages

GET /settings/payment

Returns the messages shown on the payment landing page.

Example response

{
  "message": "Please pay invoice {invoicenumber}.",
  "success_message": "Thank you for your payment."
}

PUT /settings/payment

Parameters

Parameter In Type Required Description
message body string Yes Payment-page message
success_message body string Yes Message shown after payment

Responses

Status Description
200 Request successful
400 Missing field (PUT)
500 Save failed (PUT)

Peppol settings

GET /settings/peppol

Returns whether Peppol is enabled for the company.

Example response

{ "id": 1, "company_id": 1, "enabled": true }

PUT /settings/peppol

Parameters

Parameter In Type Required Description
enabled body boolean Yes Enable/disable Peppol

Responses

Status Description
200 Request successful
500 Could not load/save Peppol settings

Terms & conditions

A company can store a single terms & conditions document.

HEAD /settings/termsandconditions — check whether a document exists (200 if so, 404 if not).

GET /settings/termsandconditions — download the stored document (binary).

POST /settings/termsandconditions — upload a document. Send exactly one file as multipart/form-data.

DELETE /settings/termsandconditions — remove the document.

Responses

Status Description
200 Success
400 No file, or more than one file uploaded (POST)
404 No terms and conditions set (HEAD/GET)
500 Storage error (POST upload / DELETE)

Email settings

Sender & CC addresses

Email can be sent from verified from addresses, optionally CC’d to cc addresses. Each address is verified before it can be used.

List addresses

GET /settings/email/address

Example response

[
  { "id": 1, "type": "from", "email": "sender@example.com", "verified": true },
  { "id": 2, "type": "cc", "email": "cc@example.com", "verified": false }
]

Add an address

POST /settings/email/address

Adds an address and sends it a verification email.

Parameter In Type Required Description
type body string Yes from or cc
email body string Yes Email address
redirect body string No URL to redirect to from the verification link

Example response

{ "id": 3, "type": "from", "email": "new@example.com", "verified": false }

Verify an address

POST /settings/email/verify

Confirms an address using the key from the verification email. (This is the target of the verification link; usually not called directly.)

Parameter In Type Required Description
email body string Yes Address being verified
key body string Yes Verification key (SHA-256)
company body integer Yes Company id

Remove an address

DELETE /settings/email/address/{id}/{type}

Parameter In Type Required Description
id path integer Yes Address id
type path string Yes from or cc

Responses

Status Description
200 Success
400 Invalid type / redirect, or invalid verify request
404 Address not found (remove)
500 Verification email could not be sent (add)

Email templates

Reusable email subject/body templates. Placeholders such as {invoicenumber}, {duedate}, {paymentlink} are substituted at send time.

List templates

GET /settings/email/text

Example response

[
  { "id": 1, "subject": "Invoice {invoicenumber}", "name": "Default", "text": "<p>Dear {companyname}...</p>" }
]

Get / create / update / delete

GET /settings/email/text/{id} — fetch one.

POST /settings/email/text — create.

PUT /settings/email/text/{id} — update.

DELETE /settings/email/text/{id} — remove.

For create and update:

Parameter In Type Required Description
subject body string Yes Email subject
name body string Yes Template name
text body string Yes Email body

Example response

{ "id": 1, "subject": "Invoice {invoicenumber}", "name": "Default", "text": "<p>Dear {companyname}...</p>" }

Responses

Status Description
200 Success
400 Validation failed
404 Template not found

SMS settings

Enable / disable SMS

GET /settings/sms

Example response

{ "id": 1, "company_id": 1, "enabled": true }

PUT /settings/sms

Parameter In Type Required Description
enabled body boolean Yes Enable/disable SMS

Returns 200 on success, or 500 if the settings could not be loaded/saved.

SMS usage

GET /settings/sms/count

Returns the number of SMS segments used last month, this month, and in total.

Example response

{ "last": 150, "current": 320, "total": 5234 }

SMS templates

Reusable SMS templates. The text must contain a payment link placeholder ({paymentlink} / {betaallink}) or an invoice link placeholder ({viewinvoice} / {bekijkfactuur}).

List templates

GET /settings/sms/text

Example response

[
  { "id": 1, "name": "Payment reminder", "text": "Please pay via {paymentlink}" }
]

Get / create / update / delete

GET /settings/sms/text/{id} — fetch one.

POST /settings/sms/text — create.

PUT /settings/sms/text/{id} — update.

DELETE /settings/sms/text/{id} — remove.

For create and update:

Parameter In Type Required Description
name body string Yes Template name
text body string Yes SMS text (must include a payment/invoice placeholder)

Example response

{ "id": 1, "name": "Payment reminder", "text": "Please pay via {paymentlink}" }

Responses

Status Description
200 Success
400 Text missing a required placeholder (create/update)
404 Template not found (get/update/delete)
500 Save/delete failed

Attachments

Reusable file attachments that can be attached to outgoing invoice/quotation emails (linked from an invoice profile’s send settings).

List attachments

GET /settings/attachments

Example response

[
  { "id": 1, "name": "General terms", "filename": "terms.pdf" }
]

Create an attachment

POST /settings/attachments

The file is uploaded as base64 in the content field.

Parameter In Type Required Description
name body string Yes Display name
filename body string Yes Original filename (with extension)
content body string Yes Base64-encoded file content

Example response

{ "id": 1, "name": "General terms", "filename": "terms.pdf" }

Update an attachment

PUT /settings/attachments/{id}

Parameter In Type Required Description
id path integer Yes Attachment id
name body string No New display name
filename body string No* New filename (required if content is sent)
content body string No* New base64 content (required if filename is sent)

Delete an attachment

DELETE /settings/attachments/{id}

Responses

Status Description
200 Success
400 Missing fields / invalid base64 (create/update)
404 Attachment not found (update/delete)
500 Storage (S3) or save/delete failure

Invoice profiles

A profile is a sender identity / branding template: sender details, logo, texts, layout and default email/SMS templates. A company can have multiple profiles, one of which is the default. Profiles are referenced as the sender when creating an invoice.

List profiles

GET /settings/profile

Returns all profiles. Each profile is a large object; key fields:

Example response (truncated)

[
  {
    "id": 1,
    "name": "Default Profile",
    "default": true,
    "logo": "1.png",
    "language": "nl",
    "expiration": 14,
    "companyName": "ACME B.V.",
    "address": "Condensatorweg 54",
    "postalCode": "1014AX",
    "city": "Amsterdam",
    "country": "NL",
    "vatNumber": "NL857494454B01",
    "email": "info@acme.com",
    "logo_position": "left",
    "sender_position": "right",
    "peppol_verified": false
  }
]

Get a profile

GET /settings/profile/{id}

{id} is a numeric id or the literal default. Returns the full profile object.

Create a profile

POST /settings/profile

Creates a profile with defaults. Returns the new profile id.

Example response

2

Update a profile

Profiles are updated through several focused endpoints. Each returns the profile id on success.

Name / language / expiration

PUT /settings/profile/{id}

Parameter In Type Required Description
name body string No Profile name
language body string No Default language, e.g. nl
expiration body integer No Invoice due period in days (default 14)

Sender details

PUT /settings/profile/{id}/sender

Parameter In Type Required Description
companyName body string Yes Sender company name
address body string Yes Street address
postalCode body string Yes Postal code
city body string Yes City
country body string Yes ISO country code (uppercase)
email body string Yes Contact email
contact body string No Contact name
vatNumber body string No VAT number
coc body string No Chamber of Commerce number
iban body string No IBAN
phone body string No Phone number
extra body string No Extra sender line
config body object Yes Which sender fields to show on the PDF

When Peppol is enabled, sender details are validated more strictly.

Texts

PUT /settings/profile/{id}/text

Parameter In Type Required Description
extratext body string No Invoice top text
extratextbottom body string No Invoice bottom text
footer body string No Invoice footer
quotationextratext body string No Quotation top text
quotationextratextbottom body string No Quotation bottom text
quotationfooter body string No Quotation footer

Invoice layout

PUT /settings/profile/{id}/invoice

Parameter In Type Required Description
logo_position body string Yes left, right or center
margin_global body integer Yes Global margin (mm)
margin_header body integer Yes Header margin (mm)
sender_position body string Yes left or right
merge_mail_attachments body boolean No Merge email attachments into the PDF

Default send settings

PUT /settings/profile/{id}/sending

Links the templates/addresses used when sending from this profile. All optional (integer ids or null): email_from_id, email_cc_id, email_text_id, email_text_reminder_id, email_text_quotation_id, sms_text_id, email_attachment_id, email_attachment_quotation_id.

Set default profile

POST /settings/profile/{id}/default

Makes the profile the company default.

GET /settings/profile/{id}.{type} — returns the logo as a base64 data URI (e.g. {id}.png).

POST or PUT /settings/profile/{id}/logo — upload a logo as multipart/form-data under the field file. Accepts PNG, JPEG, GIF, SVG.

DELETE /settings/profile/{id}/logo — remove the logo.

Delete a profile

DELETE /settings/profile/{id} — the default profile cannot be deleted.

Responses

Note the deliberate distinction: the read endpoints (list, get, logo) return 401 when the profile does not exist, while the mutating endpoints return 404.

Status Description
200 Success
400 Validation failed (create / update endpoints)
401 Profile not found (list / get / logo)
403 Default profile cannot be deleted (delete)
404 Profile not found (mutations); logo not found (GET logo)
415 Unsupported logo media type (logo upload)
500 Storage error (logo upload / profile delete)

Address verification

Verifies a profile’s sender address by post (required for some Peppol use). Operates per profile.

Status

GET /settings/verification

Example response

[
  {
    "id": 1,
    "profile_id": 5,
    "status": "verified",
    "verified_at": "2026-06-15 14:23:00",
    "attempts_remaining": 5,
    "sent_at": "2026-06-08 10:00:00",
    "can_resend": false,
    "cooldown_remaining": 345600,
    "address": "Condensatorweg 54",
    "postal_code": "1014 AX",
    "city": "Amsterdam",
    "country": "NL"
  }
]

status is one of pending, sent, verified, invalid.

Initiate

POST /settings/verification/{profileId}/initiate

Starts verification for a profile (uses its VAT/address data). Returns the verification object.

Confirm

POST /settings/verification/{id}/confirm

Parameter In Type Required Description
id path integer Yes Verification id
code body string Yes 8-character code from the letter

Example response

{
  "success": true,
  "message": "Address successfully verified",
  "verification": { "id": 2, "profile_id": 5, "status": "verified" }
}

Resend

POST /settings/verification/{id}/resend

Resends the verification letter (subject to a cooldown).

Responses

Status Description
200 Success
400 Validation failed (no sender/VAT/address, empty/invalid code)
404 Profile / verification not found
429 Already pending, resend cooldown, or max attempts exceeded
500 Could not create / reset verification

Integrations

Connections to external bookkeeping, payment and POS systems. Each integration is addressed by a {type}:

twinfield, exact, xero, yuki, visma, mollie, multisafepay, lightspeed, lsretail, lskseries, untill, thenextinvoice.

Many connectors (Twinfield, Exact, Xero, Mollie, Visma, Lightspeed*) use OAuth2: add returns a redirect URL, the provider then calls back to the callback endpoint.

List integrations

GET /settings/integrations

Parameter In Type Required Description
class query string No Filter by class: bookkeeping, payment, source, crm

Example response

[
  { "name": "twinfield", "enabled": true, "office": "123", "option": {}, "free": true, "config": {} },
  { "name": "mollie", "enabled": false, "office": null, "option": null, "free": true, "config": {} }
]

Get one integration

GET /settings/integrations/{type}

Returns the integration’s current state (disabled state if not configured).

Active bookkeeping integration

GET /settings/integrations/check

Returns the company’s active bookkeeping integration (may be inherited from a linked accountant). 404 if none is active.

Add / connect an integration

POST /settings/integrations/{type}

Body parameters depend on the connector. For OAuth2 connectors the body is usually empty (and may take an optional option object); credential-based connectors take their own fields, e.g.:

Example response (OAuth2 connector)

{ "done": false, "redirect": "https://login.twinfield.com/oauth/...", "popup": true, "openConfig": true }

For credential connectors done is typically true and no redirect is needed.

OAuth2 callback

GET /settings/integrations/callback/{type}

The redirect target for OAuth2 connectors; not called directly.

Parameter In Type Required Description
code query string Yes OAuth authorization code
state query string No OAuth state

Update an integration

PUT /settings/integrations/{type}

Saves connector configuration / mappings, e.g. ledger & VAT-code mappings for bookkeeping connectors (option[ledgernumbers], option[vatcodes], office), or product/category mappings for POS connectors. Returns a confirmation message or the updated integration.

Test an integration

GET /settings/integrations/{type}/test

Example response

{ "ok": true }

Remove an integration

DELETE /settings/integrations/{type}

Responses

Status Description
200 Success
400 OAuth / mapping / config error (callback, update)
404 Unknown type, or integration not configured/active
409 API token already exists (add thenextinvoice)
500 Setup, save, or remove failure

Company users

Manage which users have access to the current company and their role.

List users

GET /settings/user

Example response

[
  { "id": 1, "email": "owner@example.com", "name": "John Owner", "role": "owner", "language": "nl" },
  { "id": 2, "email": "staff@example.com", "name": "Jane Staff", "role": "employee", "language": "en" }
]

Add a user

POST /settings/user

Grants a user access to the company. If the email does not belong to an existing user, a new account is created (then password, name and language are required).

Parameter In Type Required Description
email body string Yes User email
role body string Yes owner or employee
password body string No* Required when creating a new account
name body string No* Required when creating a new account
language body string No* Required when creating a new account

Example response

{ "id": 3, "email": "new@example.com", "name": "New User", "language": "nl" }

Update a user’s role

PUT /settings/user/{id}

Parameter In Type Required Description
id path integer Yes User id
role body string Yes owner or employee

Remove a user

DELETE /settings/user/{id}

Removes the user from the company. The sole remaining owner cannot be removed.

Responses

Status Description
200 Success
400 Validation failed
403 User already in company / sole owner
404 User not in company

Two-factor authentication

TOTP-based 2FA for the current user.

Status

GET /settings/2fa

Example response

{ "status": "enabled" }

status is one of disabled, connecting, enabled.

Begin enrollment

POST /settings/2fa

Generates a TOTP secret and provisioning URI to show as a QR code.

Example response

{
  "secret": "abcd efgh ijkl mnop",
  "url": "otpauth://totp/TheNextInvoice:user@example.com?secret=ABCD...&issuer=TheNextInvoice"
}

Finish enrollment

PUT /settings/2fa

Confirms enrollment with a code from the authenticator app.

Parameter In Type Required Description
code body integer Yes TOTP code

Returns "Connected!" on success.

Disable

DELETE /settings/2fa

Returns "TOTP disabled".

Recovery codes

POST /settings/2fa/recovery

Generates a fresh set of one-time recovery codes (invalidating previous ones).

Example response

["abcd-efgh-ijkl", "mnop-qrst-uvwx", "yzab-cdef-ghij"]

Responses

Status Description
200 Success
400 2FA not enabled, not connecting, or invalid code
409 2FA already enabled (begin / finish)
500 Save error

Notifications

List Notifications

GET /notifications

Retrieves all notifications for the current company.

Example Response

[
    {
        "id": 42,
        "company_id": 1,
        "message": "Invoice 201800013 has been paid",
        "data": {
            "invoice_id": 12552
        },
        "time": "2019-02-28 21:30:00"
    }
]

Responses

Status Description
200 Request succesful

Dismiss Notifications

DELETE /notifications

Dismisses all notifications for the current company.

Example Response

"Notifications have been removed"

Responses

Status Description
200 Request succesful

Retrieving stream token

GET /token/lilium

Requests a token to authenticate with the notification stream service.

Example response

"thisisatoken"

Responses

Status Description
200 Request succesful