---
title: "REST API: endpoint reference · EenFactuurMaken.nl"
description: "Every endpoint of the EenFactuurMaken.nl REST API v1: invoices, customers, products, VAT, export and more, with cURL examples. Generated from the OpenAPI specification."
url: "https://eenfactuurmaken.nl/en/docs/api/reference"
markdown_url: "https://eenfactuurmaken.nl/en/docs/api/reference.md"
language: "en-GB"
---

# Endpoint reference

Every endpoint of the REST API v1 with parameters, body, responses and examples.

Every endpoint, generated from the OpenAPI specification. Want to generate a client? Use the specification directly.

Machine-readable specification (OpenAPI 3.1): `https://eenfactuurmaken.nl/api/v1/openapi.json`

## Account

The authenticated user: plan, limits and usage.

### GET /api/v1/me

Get the current user, plan, limits and usage

Parameters and responses

Responses

- `200` The caller. (application/json: Me)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/me" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Business

Your business profile (the seller on every invoice): KvK, btw-id, IBAN, numbering and defaults.

### GET /api/v1/business

Get your business profile

Parameters and responses

Responses

- `200` Business profile. (application/json: Business)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/business" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### PATCH /api/v1/business

Update your business profile

Partial update. Numbering changes are validated so numbers stay unique and gap-free. Payment details (iban, bic, bankAccountName, defaultPaymentLink) and the reply-to email can only be changed in the web app with a recent sign-in, to protect against payment-redirection fraud: with an API key or AI app (MCP/OAuth) a request that changes one of them returns 403 session\_required (sending the current value unchanged is fine). The account owner is always emailed when they change.

Parameters and responses

Body: `application/json BusinessUpdate`

Body

| Field | Type | Description |
| --- | --- | --- |
| name | string |  |
| legalName | string \| null |  |
| addressLine1 | string \| null |  |
| addressLine2 | string \| null |  |
| postalCode | string \| null |  |
| city | string \| null |  |
| country | string |  |
| kvkNumber | string \| null | KvK number (8 digits). Required on Dutch invoices. |
| vatNumber | string \| null | Your VAT number (btw-id), e.g. NL123456789B01. Required unless you use the KOR. |
| iban | string \| null | IBAN for payments. Web app session only: with an API key or AI app, changing iban, bic, bankAccountName, email or defaultPaymentLink returns 403 session\_required. |
| bic | string \| null | BIC of your bank. Web app session only (403 session\_required with an API key). |
| bankAccountName | string \| null | Account holder name. Web app session only (403 session\_required with an API key). |
| email | string (email) \| null \| "" | Reply-to address of the invoice emails. Web app session only (403 session\_required with an API key). |
| phone | string \| null |  |
| website | string \| null |  |
| peppolId | string \| null |  |
| defaultVatTreatment | "standard" \| "reverse\_charge" \| "intra\_eu" \| "export" \| "kor" \| "exempt" | Default VAT treatment. Use kor if you use the small business scheme (kleineondernemersregeling). |
| defaultSupplyType | "goods" \| "services" |  |
| defaultVatExemptionReason | string \| null |  |
| notInHandelsregister | boolean |  |
| defaultVatRate | string \| number |  |
| defaultPaymentTermDays | integer |  |
| defaultCurrency | string |  |
| defaultInvoiceLocale | "nl" \| "en" \| "de" \| "fr" |  |
| defaultNotes | string \| null |  |
| defaultFooter | string \| null |  |
| defaultPaymentLink | string (uri) \| null \| "" |  |
| defaultLayoutId | string \| null |  |
| houseStyle | object \| null |  |
| showEpcQrByDefault | boolean |  |
| invoiceNumberFormat | string | Number format. Tokens: {YYYY} {YY} {MM} {SEQ} {SEQ:n}. Example: "{YYYY}-{SEQ:4}" → 2026-0001. |
| nextInvoiceSequence | integer |  |
| creditNoteNumberFormat | string | Number format for credit notes, e.g. "C{YYYY}-{SEQ:4}". |
| nextCreditNoteSequence | integer |  |
| resetSequenceYearly | boolean | Restart the sequence at 1 every calendar year (needs a year token in the format). |
| remindersEnabled | boolean |  |
| reminderSchedule | integer\[\] | Days after the due date to send automatic reminders (Pro), e.g. \[7, 14, 30\]. |
| reminderMessage | string \| null |  |
| invoiceEmailSubject | string \| null |  |
| invoiceEmailMessage | string \| null |  |

Responses

- `200` Updated profile. (application/json: Business)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `403` Forbidden. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PATCH "https://eenfactuurmaken.nl/api/v1/business" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kvkNumber":"87654321","vatNumber":"NL001234567B89","invoiceNumberFormat":"{YYYY}-{SEQ:4}"}'
```

Request

```json
{
  "kvkNumber": "87654321",
  "vatNumber": "NL001234567B89",
  "invoiceNumberFormat": "{YYYY}-{SEQ:4}"
}
```

### GET /api/v1/business/logo

Download your logo

Parameters and responses

Responses

- `200` Image bytes. (image/png: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/business/logo" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### PUT /api/v1/business/logo

Upload your logo

multipart/form-data with a `file` field. PNG or JPEG, max 500 KB.

Parameters and responses

Body: `multipart/form-data object`

Responses

- `200` Updated profile. (application/json: Business)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `415` Unsupported media type. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PUT "https://eenfactuurmaken.nl/api/v1/business/logo" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -F "file=@logo.png"
```

### DELETE /api/v1/business/logo

Remove your logo

Parameters and responses

Responses

- `200` Updated profile. (application/json: Business)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/business/logo" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Customers

Customers (afnemers). Deleting a customer that is used on invoices archives it instead.

### GET /api/v1/customers

List customers

Search with `q` (name, email, KvK, VAT number).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| q | string (query) |  |
| page | integer (query) |  |
| pageSize | integer (query) |  |
| includeArchived | boolean \| "0" \| "1" \| "true" \| "false" \| "" (query) |  |
| archived | boolean \| "0" \| "1" \| "true" \| "false" \| "" (query) |  |

Responses

- `200` A page of customers. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/customers" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/customers

Create a customer

Parameters and responses

Body: `application/json CustomerInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| type | "business" \| "individual" | business (B2B) or individual (consumer). Default: "business". |
| name\* | string | Company or person name as printed on the invoice. |
| contactName | string \| null |  |
| email | string (email) \| null \| "" |  |
| emailCc | string \| null | Extra recipients (comma separated) for invoice emails. |
| phone | string \| null |  |
| addressLine1 | string \| null |  |
| addressLine2 | string \| null |  |
| postalCode | string \| null |  |
| city | string \| null |  |
| country | string | ISO 3166-1 alpha-2 country code. Defaults to NL. |
| kvkNumber | string \| null | KvK number (Dutch Chamber of Commerce, 8 digits). Recommended for Dutch business customers. |
| vatNumber | string \| null | Customer VAT number (btw-id / VAT ID), e.g. NL123456789B01 or DE123456789. Required for reverse\_charge and intra\_eu invoices. |
| peppolId | string \| null | Peppol participant id, e.g. 0106:12345678 (KvK) or 0190:00000000000000000000 (OIN). |
| defaultVatTreatment | "standard" \| "reverse\_charge" \| "intra\_eu" \| "export" \| "kor" \| "exempt" \| null | VAT treatment suggested for new invoices to this customer. |
| defaultSupplyType | "goods" \| "services" \| null |  |
| paymentTermDays | integer \| null | Payment term in days; overrides the business default. |
| locale | "nl" \| "en" \| "de" \| "fr" \| null | Language of invoices and emails for this customer: nl, en, de or fr. |
| notes | string \| null |  |
| defaultLayoutId | string \| null |  |

Responses

- `201` The new customer. (application/json: Customer)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/customers" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"business","name":"Bakkerij De Gouden Korst B.V.","contactName":"Sanne de Vries","email":"administratie@goudenkorst.nl","addressLine1":"Marktstraat 12","postalCode":"3511 AB","city":"Utrecht","country":"NL","kvkNumber":"12345678","vatNumber":"NL123456789B01"}'
```

Request

```json
{
  "type": "business",
  "name": "Bakkerij De Gouden Korst B.V.",
  "contactName": "Sanne de Vries",
  "email": "administratie@goudenkorst.nl",
  "addressLine1": "Marktstraat 12",
  "postalCode": "3511 AB",
  "city": "Utrecht",
  "country": "NL",
  "kvkNumber": "12345678",
  "vatNumber": "NL123456789B01"
}
```

### GET /api/v1/customers/{id}

Get a customer

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The customer. (application/json: Customer)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/customers/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### PATCH /api/v1/customers/{id}

Update a customer

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json CustomerPatch`

Body

| Field | Type | Description |
| --- | --- | --- |
| type | "business" \| "individual" |  |
| name | string |  |
| contactName | string \| null |  |
| email | string (email) \| null \| "" |  |
| emailCc | string \| null |  |
| phone | string \| null |  |
| addressLine1 | string \| null |  |
| addressLine2 | string \| null |  |
| postalCode | string \| null |  |
| city | string \| null |  |
| country | string |  |
| kvkNumber | string \| null |  |
| vatNumber | string \| null |  |
| peppolId | string \| null |  |
| defaultVatTreatment | "standard" \| "reverse\_charge" \| "intra\_eu" \| "export" \| "kor" \| "exempt" \| null |  |
| defaultSupplyType | "goods" \| "services" \| null |  |
| paymentTermDays | integer \| null |  |
| locale | "nl" \| "en" \| "de" \| "fr" \| null |  |
| notes | string \| null |  |
| defaultLayoutId | string \| null |  |
| archived | boolean |  |

Responses

- `200` The updated customer. (application/json: Customer)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PATCH "https://eenfactuurmaken.nl/api/v1/customers/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"facturen@goudenkorst.nl","paymentTermDays":30}'
```

Request

```json
{
  "email": "facturen@goudenkorst.nl",
  "paymentTermDays": 30
}
```

### DELETE /api/v1/customers/{id}

Delete (or archive) a customer

Customers used on invoices are archived instead of deleted.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` What happened. (application/json: DeleteResult)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/customers/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/vat-check

Check an EU VAT number (VIES)

Checks a customer VAT number with the EU VIES service, e.g. before an intra-EU (ICP) invoice. Rate-limited per user.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| vat\* | string (query) |  |

Responses

- `200` VIES result. (application/json: VatCheck)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/vat-check?vat=…" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

Response

```json
{
  "vatNumber": "DE123456789",
  "countryCode": "DE",
  "status": "valid",
  "checkedAt": "2026-10-12T09:30:00.000Z",
  "source": "VIES"
}
```

## Products

Products and services with a default price and VAT rate.

### GET /api/v1/products

List products

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| q | string (query) |  |
| page | integer (query) |  |
| pageSize | integer (query) |  |
| includeArchived | boolean \| "0" \| "1" \| "true" \| "false" \| "" (query) |  |
| archived | boolean \| "0" \| "1" \| "true" \| "false" \| "" (query) |  |

Responses

- `200` A page of products. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/products" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/products

Create a product

Parameters and responses

Body: `application/json ProductInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| name\* | string |  |
| description | string \| null |  |
| unit | string | Default: "C62". |
| unitPriceCents\* | integer |  |
| vatRate | string \| number |  |
| sku | string \| null |  |

Responses

- `201` The new product. (application/json: Product)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/products" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Consultancy","unit":"HUR","unitPriceCents":9500,"vatRate":"21"}'
```

Request

```json
{
  "name": "Consultancy",
  "unit": "HUR",
  "unitPriceCents": 9500,
  "vatRate": "21"
}
```

### GET /api/v1/products/{id}

Get a product

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The product. (application/json: Product)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/products/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### PATCH /api/v1/products/{id}

Update a product

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json ProductPatch`

Body

| Field | Type | Description |
| --- | --- | --- |
| name | string |  |
| description | string \| null |  |
| unit | string |  |
| unitPriceCents | integer |  |
| vatRate | string \| number |  |
| sku | string \| null |  |
| archived | boolean |  |

Responses

- `200` The updated product. (application/json: Product)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PATCH "https://eenfactuurmaken.nl/api/v1/products/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"unitPriceCents":10000}'
```

Request

```json
{
  "unitPriceCents": 10000
}
```

### DELETE /api/v1/products/{id}

Delete (or archive) a product

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` What happened. (application/json: DeleteResult)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/products/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Invoices

Invoices and credit notes: drafts, issuing (number + frozen snapshots), payment status, credit notes.

### GET /api/v1/invoices

List invoices and credit notes

`status` also accepts `overdue` (unpaid past due date) and `open` (issued or sent).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| status | "draft" \| "issued" \| "sent" \| "paid" \| "cancelled" \| "overdue" \| "open" (query) |  |
| kind | "invoice" \| "credit\_note" (query) |  |
| customerId | string (query) |  |
| q | string (query) |  |
| from | string (YYYY-MM-DD) (query) |  |
| to | string (YYYY-MM-DD) (query) |  |
| page | integer (query) |  |
| pageSize | integer (query) |  |
| sort | "issueDate" \| "-issueDate" \| "number" \| "-number" \| "total" \| "-total" \| "dueDate" \| "-dueDate" \| "createdAt" \| "-createdAt" (query) |  |

Responses

- `200` A page of invoices. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/invoices" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices

Create a draft invoice

Creates a DRAFT. Pass `customerId` or an inline `customer`. Counts against the monthly quota (Free: 5 per month); credit notes never count. Issue it with POST /invoices/{id}/issue.

Parameters and responses

Body: `application/json InvoiceInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| kind | "invoice" \| "credit\_note" | invoice (default) or credit\_note. Credit notes for an existing invoice are easier via POST /invoices/{id}/credit-note. Default: "invoice". |
| customerId | string \| null | Id of an existing customer. |
| customer | CustomerInput \| null | Inline customer data instead of customerId. A customer record is created for it. |
| number | string \| null | Normally left empty: the number is assigned when you issue. Must be unique. |
| issueDate | string (YYYY-MM-DD) | Invoice date (factuurdatum). Default: today. |
| dueDate | string (YYYY-MM-DD) | Due date. Default: issue date + payment term. |
| supplyDate | string (YYYY-MM-DD) \| null | Date of supply (leverdatum) if different from the invoice date. |
| supplyPeriodEnd | string (YYYY-MM-DD) \| null | End of the supply period, for services delivered over a period. |
| currency | string | ISO 4217 code. Default: EUR. |
| locale | "nl" \| "en" \| "de" \| "fr" | Document language: nl, en, de or fr. |
| vatTreatment | "standard" \| "reverse\_charge" \| "intra\_eu" \| "export" \| "kor" \| "exempt" | standard (default), reverse\_charge (btw verlegd, NL B2B), intra\_eu (ICP, EU B2B), export (outside the EU), kor (small business scheme), exempt. Non-standard treatments charge 0% VAT and print the required legal note. |
| supplyType | "goods" \| "services" |  |
| vatExemptionReason | string \| null |  |
| exchangeRate | string \| number \| null |  |
| exchangeRateDate | string (YYYY-MM-DD) \| null |  |
| exchangeRateSource | string \| null |  |
| creditedInvoiceId | string \| null |  |
| creditedInvoiceNumber | string \| null |  |
| creditedInvoiceDate | string (YYYY-MM-DD) \| null |  |
| reference | string \| null | Your own reference, e.g. a project or order number. |
| buyerReference | string \| null | The customer's reference (PO number / inkoopordernummer). Some Peppol receivers require it. |
| notes | string \| null | Text printed above the totals. |
| footer | string \| null | Footer text. |
| paymentLink | string (uri) \| null \| "" | Optional payment URL (e.g. a Tikkie or Mollie link) printed on the invoice. |
| showEpcQr | boolean | Print the EPC QR code (SEPA payment QR) for banking apps. |
| layoutId | string \| null | Pro: layout for this invoice (preset:classic\|modern\|minimal or a saved template id). Omit or null: the customer's layout, then the business default / house style. On Free only null or preset:classic is accepted (and the house style is used). |
| lines | InvoiceLineInput\[\] | Invoice lines (max. 500). |

Responses

- `201` The new draft, including a compliance check. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customer":{"type":"business","name":"Bakkerij De Gouden Korst B.V.","contactName":"Sanne de Vries","email":"administratie@goudenkorst.nl","addressLine1":"Marktstraat 12","postalCode":"3511 AB","city":"Utrecht","country":"NL","kvkNumber":"12345678","vatNumber":"NL123456789B01"},"issueDate":"2026-10-01","dueDate":"2026-10-15","vatTreatment":"standard","reference":"Project nieuwe website","lines":[{"description":"Webdesign en ontwikkeling – september","quantity":"12","unit":"HUR","unitPriceCents":8500,"vatRate":"21"},{"description":"Hosting en onderhoud (jaar)","quantity":"1","unit":"ANN","unitPriceCents":12000,"vatRate":"21"}]}'
```

Request

```json
{
  "customer": {
    "type": "business",
    "name": "Bakkerij De Gouden Korst B.V.",
    "contactName": "Sanne de Vries",
    "email": "administratie@goudenkorst.nl",
    "addressLine1": "Marktstraat 12",
    "postalCode": "3511 AB",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "12345678",
    "vatNumber": "NL123456789B01"
  },
  "issueDate": "2026-10-01",
  "dueDate": "2026-10-15",
  "vatTreatment": "standard",
  "reference": "Project nieuwe website",
  "lines": [
    {
      "description": "Webdesign en ontwikkeling – september",
      "quantity": "12",
      "unit": "HUR",
      "unitPriceCents": 8500,
      "vatRate": "21"
    },
    {
      "description": "Hosting en onderhoud (jaar)",
      "quantity": "1",
      "unit": "ANN",
      "unitPriceCents": 12000,
      "vatRate": "21"
    }
  ]
}
```

### GET /api/v1/invoices/next-number

Preview the next invoice and credit note numbers

Parameters and responses

Responses

- `200` Next numbers (nothing is reserved). (application/json: NextNumberPreview)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/invoices/next-number" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

Response

```json
{
  "invoice": "2026-0043",
  "creditNote": "C2026-0004"
}
```

### GET /api/v1/invoices/{id}

Get an invoice

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The invoice with lines, parties and (for drafts) the compliance check. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/invoices/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

Response

```json
{
  "id": "0f8e2a52-7d0b-4c47-9a1e-3c1b2d4e5f60",
  "kind": "invoice",
  "status": "issued",
  "displayStatus": "issued",
  "number": "2026-0042",
  "customerId": "9d2c1b0a-1234-4cde-8f00-aa11bb22cc33",
  "creditedInvoiceId": null,
  "issueDate": "2026-10-01",
  "dueDate": "2026-10-15",
  "supplyDate": null,
  "supplyPeriodEnd": null,
  "currency": "EUR",
  "locale": "nl",
  "vatTreatment": "standard",
  "reference": "Project nieuwe website",
  "buyerReference": null,
  "notes": null,
  "footer": null,
  "paymentLink": null,
  "showEpcQr": true,
  "layoutId": null,
  "subtotalCents": 114000,
  "vatTotalCents": 23940,
  "totalCents": 137940,
  "vatBreakdown": [
    {
      "category": "S",
      "rate": "21",
      "taxableCents": 114000,
      "vatCents": 23940
    }
  ],
  "issuedAt": "2026-10-01T09:12:00.000Z",
  "sentAt": null,
  "paidAt": null,
  "cancelledAt": null,
  "reminderCount": 0,
  "lastReminderAt": null,
  "recurringScheduleId": null,
  "expiresAt": null,
  "downloadedAt": "2026-10-01T09:13:00.000Z",
  "lastDownloadedAt": "2026-10-01T09:13:00.000Z",
  "createdAt": "2026-10-01T09:05:00.000Z",
  "updatedAt": "2026-10-01T09:12:00.000Z",
  "lines": [
    {
      "id": "a1",
      "position": 0,
      "productId": null,
      "description": "Webdesign en ontwikkeling – september",
      "quantity": "12",
      "unit": "HUR",
      "unitPriceCents": 8500,
      "discountPercent": "0",
      "vatRate": "21",
      "lineTotalCents": 102000
    },
    {
      "id": "a2",
      "position": 1,
      "productId": null,
      "description": "Hosting en onderhoud (jaar)",
      "quantity": "1",
      "unit": "ANN",
      "unitPriceCents": 12000,
      "discountPercent": "0",
      "vatRate": "21",
      "lineTotalCents": 12000
    }
  ],
  "seller": {
    "name": "Studio Jansen",
    "legalName": "Studio Jansen",
    "addressLine1": "Oudegracht 100",
    "postalCode": "3511 AX",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "87654321",
    "vatNumber": "NL001234567B89",
    "iban": "NL91ABNA0417164300",
    "email": "hallo@studiojansen.nl"
  },
  "sellerSnapshot": {
    "name": "Studio Jansen",
    "legalName": "Studio Jansen",
    "addressLine1": "Oudegracht 100",
    "postalCode": "3511 AX",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "87654321",
    "vatNumber": "NL001234567B89",
    "iban": "NL91ABNA0417164300",
    "email": "hallo@studiojansen.nl"
  },
  "customer": {
    "type": "business",
    "name": "Bakkerij De Gouden Korst B.V.",
    "contactName": "Sanne de Vries",
    "email": "administratie@goudenkorst.nl",
    "addressLine1": "Marktstraat 12",
    "postalCode": "3511 AB",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "12345678",
    "vatNumber": "NL123456789B01"
  },
  "customerSnapshot": {
    "type": "business",
    "name": "Bakkerij De Gouden Korst B.V.",
    "contactName": "Sanne de Vries",
    "email": "administratie@goudenkorst.nl",
    "addressLine1": "Marktstraat 12",
    "postalCode": "3511 AB",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "12345678",
    "vatNumber": "NL123456789B01"
  },
  "compliance": []
}
```

### PATCH /api/v1/invoices/{id}

Update a draft

Only drafts can be changed (409 invoice\_locked). Correct issued invoices with a credit note.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json InvoiceUpdate`

Body

| Field | Type | Description |
| --- | --- | --- |
| customerId | string \| null |  |
| customer | CustomerInput \| null |  |
| number | string \| null |  |
| issueDate | string (YYYY-MM-DD) |  |
| dueDate | string (YYYY-MM-DD) |  |
| supplyDate | string (YYYY-MM-DD) \| null |  |
| supplyPeriodEnd | string (YYYY-MM-DD) \| null |  |
| currency | string |  |
| locale | "nl" \| "en" \| "de" \| "fr" |  |
| vatTreatment | "standard" \| "reverse\_charge" \| "intra\_eu" \| "export" \| "kor" \| "exempt" |  |
| supplyType | "goods" \| "services" |  |
| vatExemptionReason | string \| null |  |
| exchangeRate | string \| number \| null |  |
| exchangeRateDate | string (YYYY-MM-DD) \| null |  |
| exchangeRateSource | string \| null |  |
| creditedInvoiceId | string \| null |  |
| creditedInvoiceNumber | string \| null |  |
| creditedInvoiceDate | string (YYYY-MM-DD) \| null |  |
| reference | string \| null |  |
| buyerReference | string \| null |  |
| notes | string \| null |  |
| footer | string \| null |  |
| paymentLink | string (uri) \| null \| "" |  |
| showEpcQr | boolean |  |
| layoutId | string \| null |  |
| lines | InvoiceLineInput\[\] |  |

Responses

- `200` The updated draft. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PATCH "https://eenfactuurmaken.nl/api/v1/invoices/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dueDate":"2026-10-31","lines":[{"description":"Webdesign en ontwikkeling – september","quantity":"12","unit":"HUR","unitPriceCents":8500,"vatRate":"21"},{"description":"Hosting en onderhoud (jaar)","quantity":"1","unit":"ANN","unitPriceCents":12000,"vatRate":"21"}]}'
```

Request

```json
{
  "dueDate": "2026-10-31",
  "lines": [
    {
      "description": "Webdesign en ontwikkeling – september",
      "quantity": "12",
      "unit": "HUR",
      "unitPriceCents": 8500,
      "vatRate": "21"
    },
    {
      "description": "Hosting en onderhoud (jaar)",
      "quantity": "1",
      "unit": "ANN",
      "unitPriceCents": 12000,
      "vatRate": "21"
    }
  ]
}
```

### DELETE /api/v1/invoices/{id}

Delete a draft

Only drafts can be deleted (409 invoice\_locked). Issued invoices are kept (Dutch retention duty); cancel or credit them instead. Deleting a draft invoice gives its unit of the monthly quota back.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` Deleted. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/invoices/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices/{id}/duplicate

Duplicate into a new draft

Copies lines and settings into a new draft with today's dates and no number. Counts against the quota (credit notes do not).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json DuplicateInvoice`

Body

| Field | Type | Description |
| --- | --- | --- |
| issueDate | string (YYYY-MM-DD) |  |
| dueDate | string (YYYY-MM-DD) |  |
| supplyDate | string (YYYY-MM-DD) \| null |  |
| supplyPeriodEnd | string (YYYY-MM-DD) \| null |  |

Responses

- `201` The new draft. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/duplicate" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices/{id}/issue

Issue a draft

Runs the Dutch compliance check (422 invoice\_not\_compliant with details), assigns the next number (gap-free), freezes seller and customer data and locks the invoice.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json IssueInvoice`

Body

| Field | Type | Description |
| --- | --- | --- |
| number | string \| null | Override the generated number (must be unique). |
| issueDate | string (YYYY-MM-DD) | Override the invoice date; the due date moves along. |

Responses

- `200` The issued invoice. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/issue" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

Response

```json
{
  "id": "0f8e2a52-7d0b-4c47-9a1e-3c1b2d4e5f60",
  "kind": "invoice",
  "status": "issued",
  "displayStatus": "issued",
  "number": "2026-0042",
  "customerId": "9d2c1b0a-1234-4cde-8f00-aa11bb22cc33",
  "creditedInvoiceId": null,
  "issueDate": "2026-10-01",
  "dueDate": "2026-10-15",
  "supplyDate": null,
  "supplyPeriodEnd": null,
  "currency": "EUR",
  "locale": "nl",
  "vatTreatment": "standard",
  "reference": "Project nieuwe website",
  "buyerReference": null,
  "notes": null,
  "footer": null,
  "paymentLink": null,
  "showEpcQr": true,
  "layoutId": null,
  "subtotalCents": 114000,
  "vatTotalCents": 23940,
  "totalCents": 137940,
  "vatBreakdown": [
    {
      "category": "S",
      "rate": "21",
      "taxableCents": 114000,
      "vatCents": 23940
    }
  ],
  "issuedAt": "2026-10-01T09:12:00.000Z",
  "sentAt": null,
  "paidAt": null,
  "cancelledAt": null,
  "reminderCount": 0,
  "lastReminderAt": null,
  "recurringScheduleId": null,
  "expiresAt": null,
  "downloadedAt": "2026-10-01T09:13:00.000Z",
  "lastDownloadedAt": "2026-10-01T09:13:00.000Z",
  "createdAt": "2026-10-01T09:05:00.000Z",
  "updatedAt": "2026-10-01T09:12:00.000Z",
  "lines": [
    {
      "id": "a1",
      "position": 0,
      "productId": null,
      "description": "Webdesign en ontwikkeling – september",
      "quantity": "12",
      "unit": "HUR",
      "unitPriceCents": 8500,
      "discountPercent": "0",
      "vatRate": "21",
      "lineTotalCents": 102000
    },
    {
      "id": "a2",
      "position": 1,
      "productId": null,
      "description": "Hosting en onderhoud (jaar)",
      "quantity": "1",
      "unit": "ANN",
      "unitPriceCents": 12000,
      "discountPercent": "0",
      "vatRate": "21",
      "lineTotalCents": 12000
    }
  ],
  "seller": {
    "name": "Studio Jansen",
    "legalName": "Studio Jansen",
    "addressLine1": "Oudegracht 100",
    "postalCode": "3511 AX",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "87654321",
    "vatNumber": "NL001234567B89",
    "iban": "NL91ABNA0417164300",
    "email": "hallo@studiojansen.nl"
  },
  "sellerSnapshot": {
    "name": "Studio Jansen",
    "legalName": "Studio Jansen",
    "addressLine1": "Oudegracht 100",
    "postalCode": "3511 AX",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "87654321",
    "vatNumber": "NL001234567B89",
    "iban": "NL91ABNA0417164300",
    "email": "hallo@studiojansen.nl"
  },
  "customer": {
    "type": "business",
    "name": "Bakkerij De Gouden Korst B.V.",
    "contactName": "Sanne de Vries",
    "email": "administratie@goudenkorst.nl",
    "addressLine1": "Marktstraat 12",
    "postalCode": "3511 AB",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "12345678",
    "vatNumber": "NL123456789B01"
  },
  "customerSnapshot": {
    "type": "business",
    "name": "Bakkerij De Gouden Korst B.V.",
    "contactName": "Sanne de Vries",
    "email": "administratie@goudenkorst.nl",
    "addressLine1": "Marktstraat 12",
    "postalCode": "3511 AB",
    "city": "Utrecht",
    "country": "NL",
    "kvkNumber": "12345678",
    "vatNumber": "NL123456789B01"
  },
  "compliance": []
}
```

### POST /api/v1/invoices/{id}/credit-note

Create a credit note for an invoice

Creates a DRAFT credit note (creditfactuur) that references the invoice, with the same lines (full credit). Edit the lines for a partial credit, then issue it. Credit notes do not count against the monthly quota.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `201` The draft credit note. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/credit-note" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices/{id}/mark-paid

Mark as paid

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json MarkPaid`

Body

| Field | Type | Description |
| --- | --- | --- |
| paidAt | string (YYYY-MM-DD) |  |

Responses

- `200` The invoice. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/mark-paid" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"paidAt":"2026-10-10"}'
```

Request

```json
{
  "paidAt": "2026-10-10"
}
```

### POST /api/v1/invoices/{id}/mark-unpaid

Mark as unpaid

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The invoice. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/mark-unpaid" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices/{id}/public-link

Create or replace the public invoice link

Issued invoices get a public "view online" page (`publicUrl`, /i/{token}) when they are issued: the customer can pay, download the PDF/UBL, and views are recorded (`firstViewedAt`, `viewedAt`, `viewCount`). Without a body this (re)creates a missing link; `{ "regenerate": true }` replaces the token so the old link stops working. Drafts → 409 invoice\_not\_issued.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json PublicLink`

Body

| Field | Type | Description |
| --- | --- | --- |
| regenerate | boolean |  |

Responses

- `200` The invoice with its new publicUrl. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/public-link" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"regenerate":true}'
```

Request

```json
{
  "regenerate": true
}
```

### DELETE /api/v1/invoices/{id}/public-link

Switch the public invoice link off

The public page and its downloads return 404 from now on. Create a new link with POST.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The invoice (publicUrl: null). (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/invoices/{id}/public-link" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices/{id}/cancel

Cancel an issued invoice

Only issued/sent unpaid invoices. The number stays used. For paid invoices, create a credit note. An invoice that already has an issued credit note can't be cancelled any more (409 invoice\_has\_credit\_notes, details.creditNotes): the credit note is the correction.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The cancelled invoice. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/cancel" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/exchange-rate

ECB exchange rate for a currency

Euro reference rate of the European Central Bank (EUR per unit) for invoices in another currency; `date` defaults to today. 422 exchange\_rate\_unknown\_currency, 503 exchange\_rate\_unavailable (enter the rate manually).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| currency\* | string (query) |  |
| date | string (YYYY-MM-DD) (query) |  |

Responses

- `200` The rate. (application/json: ExchangeRate)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)
- `503` An external service (VIES, ECB, Stripe) is unavailable. Try again later. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/exchange-rate?currency=…" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

Response

```json
{
  "currency": "USD",
  "date": "2026-10-12",
  "rateDate": "2026-10-09",
  "ecbRate": "1.1298",
  "rate": "0.8851124093",
  "source": "ECB"
}
```

## Documents

PDF and UBL (Peppol BIS Billing 3.0 / NLCIUS) files, previews and compliance checks.

### GET /api/v1/invoices/{id}/pdf

Download the PDF

`?download=1` sets Content-Disposition: attachment and marks an issued document as downloaded (`downloadedAt`). Drafts get a "concept" watermark.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |
| download | "0" \| "1" \| "true" \| "false" (query) |  |
| embedUbl | "0" \| "1" \| "true" \| "false" (query) |  |

Responses

- `200` PDF file. (application/pdf: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/invoices/{id}/pdf" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/invoices/{id}/ubl

Download the UBL e-invoice

UBL 2.1, Peppol BIS Billing 3.0 / NLCIUS. Issued invoices only (409 invoice\_not\_issued). Dutch sellers need a KvK number for Peppol (409 peppol\_kvk\_required). Credit notes produce a CreditNote document. Marks the document as downloaded (`downloadedAt`).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |
| download | "0" \| "1" \| "true" \| "false" (query) |  |
| embedPdf | "0" \| "1" \| "true" \| "false" (query) |  |

Responses

- `200` UBL XML. (application/xml: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/invoices/{id}/ubl" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/invoices/{id}/compliance

Check the Dutch invoice requirements

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` Issues (empty = compliant). (application/json: ComplianceIssue\[\])
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/invoices/{id}/compliance" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/invoices/preview-pdf

Render a preview PDF

Renders an unsaved invoice and/or layout. Nothing is stored. Any plan may preview custom layouts.

Parameters and responses

Body: `application/json PreviewPdf`

Body

| Field | Type | Description |
| --- | --- | --- |
| invoice | InvoiceInput \| null |  |
| layoutId | string \| null |  |
| layout | LayoutConfig \| null |  |

Responses

- `200` PDF with a draft watermark. (application/pdf: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/preview-pdf" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/public/invoices/{token}

Public

Public invoice page data

What the customer sees on /i/{token}: display-ready, in the invoice language. 404 when the token is unknown or the link was switched off. Rate-limited per IP.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {token}\* | string (path) | Signed token from get\_invoice\_download\_links. |

Responses

- `200` The public invoice. (application/json: PublicInvoice)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/public/invoices/{token}"
```

### GET /api/public/invoices/{token}/pdf

Public

Public invoice PDF

PDF (with the UBL embedded). `?download=1` → attachment.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {token}\* | string (path) | Signed token from get\_invoice\_download\_links. |
| download | "0" \| "1" \| "true" \| "false" (query) |  |

Responses

- `200` PDF file. (application/pdf: string)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/public/invoices/{token}/pdf"
```

### GET /api/public/invoices/{token}/ubl

Public

Public invoice UBL e-invoice

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {token}\* | string (path) | Signed token from get\_invoice\_download\_links. |

Responses

- `200` UBL XML. (application/xml: string)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/public/invoices/{token}/ubl"
```

### POST /api/public/invoices/{token}/view

Public

Record a view of the public page

Called by the page in the browser. Not counted when the signed-in owner opens their own page; repeated views within 30 minutes count once.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {token}\* | string (path) | Signed token from get\_invoice\_download\_links. |

Responses

- `200` Whether the view was recorded. (application/json: object)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/public/invoices/{token}/view"
```

## Sending

Email invoices (PDF + UBL attached) and payment reminders.

### POST /api/v1/invoices/{id}/send

Email the invoice

Emails the PDF and (unless `attachUbl` is false) the UBL e-invoice as attachments, to the customer email or `to`. Issued invoices only (409 invoice\_not\_issued). Status becomes `sent`. 422 no\_recipient when there is no address. Limits: Free can only email the customer's own address(es) on file (email + CC) or the account email (403 email\_recipient\_not\_allowed, `details.addresses`) and at most 30 recipients (to + cc) per 24 hours (429 email\_daily\_limit); Pro at most 500 recipients per hour (429 email\_rate\_limited). The same document can be emailed at most 5 times per 24 hours (429 invoice\_resend\_limit). Sending pauses for up to 7 days when recent emails of the account bounced or were marked as spam (403 email\_sending\_paused), and a platform-wide daily cap can apply (429 email\_platform\_limit).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json SendInvoice`

Body

| Field | Type | Description |
| --- | --- | --- |
| to | string (email)\[\] | Recipients. Default: the customer email. |
| cc | string (email)\[\] | CC recipients. |
| subject | string \| null | Subject. Default: your template. |
| message | string \| null | Message body (plain text). Default: your template. |
| attachUbl | boolean | Attach the UBL e-invoice next to the PDF. Default: true. |

Responses

- `200` The invoice after sending. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `403` Forbidden. (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/send" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Hoi Sanne,\n\nIn de bijlage vind je de factuur voor september.\n\nGroet, Pieter"}'
```

Request

```json
{
  "message": "Hoi Sanne,\n\nIn de bijlage vind je de factuur voor september.\n\nGroet, Pieter"
}
```

### POST /api/v1/invoices/{id}/remind

Send a payment reminder now

Emails a friendly payment reminder (betalingsherinnering) for an open (issued/sent, unpaid) invoice. 409 invoice\_not\_remindable / reminder\_too\_early, 422 no\_recipient.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The invoice (reminderCount and lastReminderAt updated). (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/invoices/{id}/remind" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Reports

Dashboard KPIs, the btw-aangifte helper and the ZIP export.

### GET /api/v1/stats

Dashboard KPIs and VAT per quarter

Open, overdue and paid amounts, revenue this year, and the btw-aangifte helper (rubrieken 1a–3b, plus exempt/KOR turnover) for this and the previous quarter. Pass `quarter` (and optionally `year`, default this year) to also get any other quarter in `vat.selected`.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| year | integer (query) |  |
| quarter | integer (query) |  |

Responses

- `200` Statistics. (application/json: Stats)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/stats" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/export

Pro

Export everything as ZIP

PDF + UBL for every issued invoice and credit note, plus invoices.csv. Optional issue date range. Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| from | string (YYYY-MM-DD) (query) |  |
| to | string (YYYY-MM-DD) (query) |  |

Responses

- `200` ZIP archive (streamed). (application/zip: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/export" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/export/expiring

Download the invoices that will be deleted soon

Every plan. Same ZIP as /export (PDF + UBL + invoices.csv) with the issued invoices and credit notes that the free-plan retention deletes within 30 days. Marks them as downloaded (`downloadedAt`). You remain responsible for the 7-year retention (bewaarplicht).

Parameters and responses

Responses

- `200` ZIP archive (streamed). (application/zip: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/export/expiring" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Layouts

Invoice layouts. Every plan: the house style (PATCH /api/v1/business \`houseStyle\`: classic/modern/minimal template, colours, logo placement, QR and payment link) and the built-in presets as the account default. Pro: saved templates (fonts, page size, header/footer texts, label overrides) and choosing a layout per customer or per invoice.

### GET /api/v1/layouts

List layouts

Parameters and responses

Responses

- `200` Built-in presets and your saved templates (custom layouts). The house style is on GET /api/v1/business (`houseStyle`). (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/layouts" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/layouts

Pro

Create a saved template (custom layout)

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Body: `application/json LayoutInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| name\* | string |  |
| config\* | LayoutConfig |  |

Responses

- `201` The new layout. (application/json: Layout)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/layouts" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/layouts/{id}

Get a layout

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The layout. (application/json: Layout)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/layouts/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### PATCH /api/v1/layouts/{id}

Pro

Update a saved template (custom layout)

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json LayoutUpdate`

Body

| Field | Type | Description |
| --- | --- | --- |
| name | string |  |
| config | object |  |

Responses

- `200` The updated layout. (application/json: Layout)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PATCH "https://eenfactuurmaken.nl/api/v1/layouts/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### DELETE /api/v1/layouts/{id}

Delete a custom layout

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` Deleted. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/layouts/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Recurring

Recurring invoices (Pro).

### GET /api/v1/recurring

Pro

List recurring invoices

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| page | integer (query) |  |
| pageSize | integer (query) |  |

Responses

- `200` Your schedules. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/recurring" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/recurring

Pro

Create a recurring invoice

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Body: `application/json RecurringInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| templateInvoiceId\* | string |  |
| frequency\* | "weekly" \| "monthly" \| "quarterly" \| "yearly" |  |
| interval | integer | Default: 1. |
| nextRunDate\* | string (YYYY-MM-DD) |  |
| endDate | string (YYYY-MM-DD) \| null |  |
| autoIssue | boolean | Default: true. |
| autoSend | boolean | Default: false. |
| active | boolean | Default: true. |

Responses

- `201` The schedule. (application/json: RecurringSchedule)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/recurring" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"templateInvoiceId":"0f8e2a52-7d0b-4c47-9a1e-3c1b2d4e5f60","frequency":"monthly","interval":1,"nextRunDate":"2026-11-01","autoIssue":true,"autoSend":true}'
```

Request

```json
{
  "templateInvoiceId": "0f8e2a52-7d0b-4c47-9a1e-3c1b2d4e5f60",
  "frequency": "monthly",
  "interval": 1,
  "nextRunDate": "2026-11-01",
  "autoIssue": true,
  "autoSend": true
}
```

### GET /api/v1/recurring/{id}

Pro

Get a recurring invoice

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The schedule. (application/json: RecurringSchedule)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/recurring/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### PATCH /api/v1/recurring/{id}

Pro

Update a recurring invoice

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Body: `application/json RecurringUpdate`

Body

| Field | Type | Description |
| --- | --- | --- |
| frequency | "weekly" \| "monthly" \| "quarterly" \| "yearly" |  |
| interval | integer |  |
| nextRunDate | string (YYYY-MM-DD) |  |
| endDate | string (YYYY-MM-DD) \| null |  |
| autoIssue | boolean |  |
| autoSend | boolean |  |
| active | boolean |  |

Responses

- `200` The schedule. (application/json: RecurringSchedule)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X PATCH "https://eenfactuurmaken.nl/api/v1/recurring/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### DELETE /api/v1/recurring/{id}

Pro

Delete a recurring invoice

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` Deleted. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/recurring/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/recurring/{id}/run-now

Pro

Run a recurring invoice now

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The created invoice. (application/json: Invoice)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/recurring/{id}/run-now" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Email

Custom sender domains (Pro) and the outbound email log.

### GET /api/v1/email-domains

Pro

List sender domains

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Responses

- `200` Your domains. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/email-domains" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/email-domains

Pro

Add a sender domain

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Body: `application/json EmailDomainInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| domain\* | string |  |
| fromLocalPart | string | Default: "facturen". |
| fromName | string \| null |  |

Responses

- `201` The domain with the DNS records to add. (application/json: EmailDomain)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/email-domains" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"studiojansen.nl","fromLocalPart":"facturen","fromName":"Studio Jansen"}'
```

Request

```json
{
  "domain": "studiojansen.nl",
  "fromLocalPart": "facturen",
  "fromName": "Studio Jansen"
}
```

### GET /api/v1/email-domains/{id}

Pro

Get a sender domain

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The domain. (application/json: EmailDomain)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/email-domains/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### DELETE /api/v1/email-domains/{id}

Pro

Remove a sender domain

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` Deleted. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X DELETE "https://eenfactuurmaken.nl/api/v1/email-domains/{id}" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/email-domains/{id}/verify

Pro

Check the DNS records now

Requires the \*\*Pro\*\* plan (402 `plan_upgrade_required` on Free).

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` The domain with updated status. (application/json: EmailDomain)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `402` Plan limit reached (`plan_limit_reached`) or Pro needed (`plan_upgrade_required`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/email-domains/{id}/verify" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/emails

Outbound email log

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| page | integer (query) |  |
| pageSize | integer (query) |  |

Responses

- `200` Sent emails, newest first. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/emails" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

## Billing

Your EenFactuurMaken.nl subscription (paid through Stripe; our own invoices for every payment).

### GET /api/v1/billing

Subscription and payments

Parameters and responses

Responses

- `200` Subscription. (application/json: Billing)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/billing" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/billing/checkout

Start a Pro checkout

Parameters and responses

Body: `application/json CheckoutInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| interval\* | "month" \| "year" |  |
| locale | "nl" \| "en" | Language of the Stripe Checkout page and the return page. |

Responses

- `200` Checkout URL. (application/json: CheckoutResult)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `422` Validation failed (`validation_failed`, details = issues) or the invoice does not meet the Dutch requirements (`invoice_not_compliant`, details = ComplianceIssue\[\]). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/billing/checkout" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"interval":"month"}'
```

Request

```json
{
  "interval": "month"
}
```

### POST /api/v1/billing/cancel

Cancel Pro at the end of the period

Web app session only (403 session\_required with an API key).

Parameters and responses

Responses

- `200` Subscription. (application/json: Billing)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `403` Forbidden. (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/billing/cancel" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/billing/resume

Undo a cancellation

Restarts renewals before Pro ends (Stripe cancel\_at\_period\_end = false). 409 not\_cancelled / subscription\_ended (start a new checkout). Web app session only.

Parameters and responses

Responses

- `200` Subscription. (application/json: Billing)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `403` Forbidden. (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/billing/resume" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/billing/sync

Re-check the checkout and subscription

Asks Stripe for the status of the open checkout session, or of the running subscription and its latest invoice (the return page polls this).

Parameters and responses

Responses

- `200` Subscription. (application/json: Billing)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/billing/sync" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /api/v1/billing/payments/{id}/invoice

Download our invoice for a payment

The EenFactuurMaken.nl invoice (PDF) for a paid subscription payment.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {id}\* | string (path) | Resource id. |

Responses

- `200` PDF file. (application/pdf: string)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/billing/payments/{id}/invoice" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### POST /api/v1/billing/portal

Open the Stripe billing portal

Short-lived URL of the Stripe billing portal: update the payment method or cancel at the end of the period. Web app session only (403 session\_required with an API key). 409 no\_billing\_account without a running subscription.

Parameters and responses

Body: `application/json BillingPortalInput`

Body

| Field | Type | Description |
| --- | --- | --- |
| locale | "nl" \| "en" | Language of the billing portal and the return page. |

Responses

- `200` Portal URL. (application/json: BillingPortalResult)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `403` Forbidden. (application/json: Error)
- `409` Conflict with the current state, e.g. `invoice_locked`, `invoice_not_issued`, `number_taken`, `invoice_has_credit_notes`, `invoice_fully_credited`, `peppol_kvk_required`. Each operation lists its own codes. (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/v1/billing/portal" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"locale":"nl"}'
```

Request

```json
{
  "locale": "nl"
}
```

### POST /api/webhooks/stripe

Public

Stripe webhook (for Stripe only)

Called by Stripe. Requires a valid Stripe-Signature header over the raw body (400 otherwise). The object is re-fetched from the Stripe API, so the body is never trusted; events are idempotent per event id.

Parameters and responses

Body: `application/json object`

Responses

- `200` Acknowledged. (application/json: object)
- `400` Bad request. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/api/webhooks/stripe"
```

## AI

Machine-readable entry points: this spec, the MCP server and signed download links.

### GET /api/v1/openapi.json

Public

This OpenAPI 3.1 document

Parameters and responses

Responses

- `200` OpenAPI document. (application/json: object)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/api/v1/openapi.json"
```

### POST /mcp

MCP server (Streamable HTTP)

Model Context Protocol endpoint for AI assistants (ChatGPT, Claude, Gemini, Cursor, VS Code). JSON-RPC 2.0 over Streamable HTTP, stateless. Authenticate with an API key or an OAuth access token. See /docs/mcp.

Parameters and responses

Body: `application/json object`

Responses

- `200` JSON-RPC response. (application/json: object)
- `401` Missing or invalid API key (`unauthenticated`, `invalid_api_key`). (application/json: Error)
- `429` Too many requests (`rate_limited`). Wait the number of seconds in the `Retry-After` header. (application/json: Error)

Example

cURL

```bash
curl -X POST "https://eenfactuurmaken.nl/mcp" \
  -H "Authorization: Bearer $EENFACTUURMAKEN_API_KEY"
```

### GET /d/{token}

Public

Signed download link

Short-lived link (default 15 minutes) to an invoice PDF or UBL file, created by the MCP tool get\_invoice\_download\_links. No authentication: the token is signed. 410 link\_expired after expiry.

Parameters and responses

Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| {token}\* | string (path) | Signed token from get\_invoice\_download\_links. |

Responses

- `200` The file. (application/pdf: string)
- `404` Not found (also for resources of other accounts). (application/json: Error)
- `410` Gone (expired link). (application/json: Error)

Example

cURL

```bash
curl "https://eenfactuurmaken.nl/d/{token}"
```

Last updated on 13 October 2026
