The technical reference shares its source with OpenAPI. Expand an operation or schema to inspect parameters, fields and error codes.
get
/api/v1/clients
Find clients
Clients in the account default administration, newest first. Search name/email with q. No pagination. Ask the user when multiple clients match.
Required permission: clients:read
Parameters
[
{
"name": "q",
"in": "query",
"required": false,
"description": "Case-insensitive name or email search.",
"schema": {
"type": "string"
}
}
]
Responses and errors
{
"200": {
"description": "Matching clients.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"clients": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Client"
}
}
},
"required": [
"clients"
]
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
post
/api/v1/clients
Create a client
Creates a saved client. Omit number to assign the next client number. Search first to avoid duplicates; name/email are not unique. Retries are not deduplicated. Individual clients discard business-only fields.
Required permission: clients:write
JSON request · required
{
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ClientCreate"
},
"example": {
"name": "Acme B.V.",
"email": "administratie@example.com",
"type": "business",
"vatMode": "standard"
}
}
}
}
Responses and errors
{
"201": {
"description": "Created client; null if removed before read-back.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"client": {
"anyOf": [
{
"$ref": "#/components/schemas/Client"
},
{
"type": "null"
}
]
}
},
"required": [
"client"
]
}
}
}
},
"400": {
"description": "invalid_json or validation; issues groups messages by top-level field.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "duplicate_number: client number already exists or automatic numbering could not find a free number.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
get
/api/v1/clients/{id}
Read a client
Reads a client in the account default administration.
Required permission: clients:read
Parameters
[
{
"name": "id",
"in": "path",
"required": true,
"description": "ID returned by this API.",
"schema": {
"type": "string",
"minLength": 1
}
}
]
Responses and errors
{
"200": {
"description": "Client.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"client": {
"$ref": "#/components/schemas/Client"
}
},
"required": [
"client"
]
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "not_found: unknown client or outside this administration.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
get
/api/v1/invoices
List invoices
Invoices in the account default administration, newest first. Includes drafts and existing credit invoices.
Required permission: invoices:read
Parameters
[
{
"name": "clientId",
"in": "query",
"required": false,
"description": "Only invoices linked to this client.",
"schema": {
"type": "string"
}
},
{
"name": "status",
"in": "query",
"required": false,
"description": "Derived display status; overdue can include an unsent draft.",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"enum": [
"draft",
"final",
"sent",
"overdue",
"paid"
],
"description": "Derived display status. An unpaid draft with a past due date can be overdue."
}
},
{
"name": "limit",
"in": "query",
"required": false,
"description": "Page size.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 50
}
},
{
"name": "offset",
"in": "query",
"required": false,
"description": "Number of rows to skip.",
"schema": {
"type": "integer",
"minimum": 0,
"default": 0
}
}
]
Responses and errors
{
"200": {
"description": "Page of invoices.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"invoices": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Invoice"
}
},
"total": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"offset": {
"type": "integer"
}
},
"required": [
"invoices",
"total",
"limit",
"offset"
]
}
}
}
},
"400": {
"description": "invalid_status: unsupported status filter.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
post
/api/v1/invoices
Create a draft or append lines
Creates an unnumbered draft for a saved client. Sender, bank account and payment term come from account settings; VAT treatment comes from the client. The server calculates totals. Use onOpenConcept=create for a separate draft. append may change a draft already scheduled for delivery. Creation/appending is NOT idempotent; an Idempotency-Key header is not supported. Dates and notes are ignored on append. A successful draft can still need details before finalization; review it in the app.
Required permission: invoices:write
JSON request · required
{
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvoiceCreate"
},
"example": {
"clientId": "REPLACE_WITH_CLIENT_ID",
"onOpenConcept": "create",
"lines": [
{
"description": "Advies",
"quantity": 8,
"unit": "uur",
"unitPriceCents": 9500,
"vatRate": 21
}
]
}
}
}
}
Responses and errors
{
"200": {
"description": "Lines appended; appended=true. sendPlanned=true means these lines will join an existing scheduled send.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"invoice": {
"anyOf": [
{
"$ref": "#/components/schemas/InvoiceDetail"
},
{
"type": "null"
}
]
},
"appended": {
"type": "boolean"
},
"sendPlanned": {
"type": "boolean"
}
},
"required": [
"invoice",
"appended",
"sendPlanned"
]
}
}
}
},
"201": {
"description": "New draft; appended=false and sendPlanned=false. invoice can be null if removed before read-back.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"invoice": {
"anyOf": [
{
"$ref": "#/components/schemas/InvoiceDetail"
},
{
"type": "null"
}
]
},
"appended": {
"type": "boolean"
},
"sendPlanned": {
"type": "boolean"
}
},
"required": [
"invoice",
"appended",
"sendPlanned"
]
}
}
}
},
"400": {
"description": "invalid_json or validation; issues groups messages by top-level field.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"402": {
"description": "invoice_cap: account invoice allowance reached.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "client_not_found: unknown client or outside this administration.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"422": {
"description": "no_company_profile: complete company settings in the app.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
get
/api/v1/invoices/{id}
Read an invoice with lines
Returns the invoice and calculated totals. No PDF, UBL, public recipient token or download URL is returned.
Required permission: invoices:read
Parameters
[
{
"name": "id",
"in": "path",
"required": true,
"description": "ID returned by this API.",
"schema": {
"type": "string",
"minLength": 1
}
}
]
Responses and errors
{
"200": {
"description": "Invoice with lines.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"invoice": {
"$ref": "#/components/schemas/InvoiceDetail"
}
},
"required": [
"invoice"
]
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "not_found: unknown invoice or outside this administration.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
post
/api/v1/invoices/{id}/send
Schedule invoice delivery
Schedules a real email to the recipient. Only call when the user requests delivery. mode=now schedules after the 20-second undo window; delivery runs asynchronously. mode=scheduled (also an empty request body) defaults to tomorrow in the account send window. date may be at most 180 days ahead. An existing schedule is preserved with alreadyPlanned=true. After dispatch a repeat may return not_a_draft. A successful response means scheduled, not delivered. The invoice is numbered during dispatch if it is not already finalized.
Required permission: invoices:send
Parameters
[
{
"name": "id",
"in": "path",
"required": true,
"description": "ID returned by this API.",
"schema": {
"type": "string",
"minLength": 1
}
}
]
JSON request · optional
{
"required": false,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvoiceSend"
},
"example": {
"mode": "scheduled"
}
}
}
}
Responses and errors
{
"200": {
"description": "Delivery scheduled or existing schedule preserved.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"scheduledSendAt": {
"type": "string",
"format": "date-time"
},
"alreadyPlanned": {
"type": "boolean"
}
},
"required": [
"scheduledSendAt",
"alreadyPlanned"
]
}
}
}
},
"400": {
"description": "invalid_json, validation or date_too_far.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "invalid_api_key: missing, malformed, unknown or revoked Bearer key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "subscription_inactive, plan_not_allowed or insufficient_scope.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "not_found.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "not_a_draft: invoice has already been sent or paid.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"422": {
"description": "email_required: recipient has no email address.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
ClientCreate · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"type": {
"default": "business",
"type": "string",
"enum": [
"business",
"individual"
]
},
"name": {
"type": "string",
"minLength": 1
},
"number": {
"type": "string",
"maxLength": 40
},
"vatMode": {
"default": "standard",
"type": "string",
"enum": [
"standard",
"reverse_charge",
"vat_exempt"
]
},
"bankAccountId": {
"description": "An existing bank account ID from this administration. Omit to use the default; this API does not list bank accounts.",
"type": "string"
},
"contactName": {
"type": "string"
},
"email": {
"anyOf": [
{
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
},
{
"type": "string",
"const": ""
}
]
},
"address": {
"type": "string"
},
"postalCode": {
"type": "string"
},
"city": {
"type": "string"
},
"country": {
"description": "Country name. Omitted or empty defaults to Nederland.",
"type": "string"
},
"kvk": {
"description": "Optional KVK number: eight digits, ignoring whitespace.",
"type": "string"
},
"vatNumber": {
"description": "Optional VAT number. NL numbers must match NL + 9 digits + B + 2 digits (ignoring spaces/dots/case); foreign prefixes are accepted.",
"type": "string"
}
},
"required": [
"name"
]
}
InvoiceCreate · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"clientId": {
"type": "string",
"minLength": 1,
"description": "ID returned by the clients API. Do not invent IDs."
},
"lines": {
"minItems": 1,
"maxItems": 200,
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string",
"minLength": 1
},
"details": {
"type": "string",
"maxLength": 2000
},
"quantity": {
"description": "Quantity; rounded server-side to 3 decimal places.",
"type": "number",
"minimum": 0.001,
"maximum": 100000
},
"unit": {
"type": "string",
"maxLength": 30
},
"unitPriceCents": {
"type": "integer",
"minimum": 0,
"maximum": 100000000,
"description": "Unit price excluding VAT, in integer euro cents. 9500 means EUR 95.00."
},
"vatRate": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"enum": [
21,
9,
0
],
"description": "VAT percentage; the client VAT mode determines whether VAT is charged."
},
"discountPct": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"periodStart": {
"type": "string",
"minLength": 1
},
"periodEnd": {
"description": "ISO timestamp; exclusive end of the service period.",
"type": "string",
"minLength": 1
}
},
"required": [
"description",
"quantity",
"unitPriceCents",
"vatRate"
]
}
},
"issueDate": {
"description": "YYYY-MM-DD. Defaults to today in UTC. Ignored when appending.",
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"dueDate": {
"description": "YYYY-MM-DD. Defaults to issueDate plus the account payment term. Ignored when appending.",
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"notes": {
"description": "Invoice notes. Ignored when appending to an existing draft.",
"type": "string",
"maxLength": 2000
},
"onOpenConcept": {
"default": "create",
"description": "create always makes a new draft. append adds lines to an existing open draft (even one scheduled for delivery), or creates one if none exists. Neither mode deduplicates retries.",
"type": "string",
"enum": [
"create",
"append"
]
}
},
"required": [
"clientId",
"lines"
]
}
InvoiceSend · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"mode": {
"default": "scheduled",
"type": "string",
"enum": [
"now",
"scheduled"
]
},
"date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
}
}
Client · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"business",
"individual"
]
},
"number": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"name": {
"type": "string"
},
"contactName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"email": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"address": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"postalCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"city": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"kvk": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"vatNumber": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"vatMode": {
"type": "string",
"enum": [
"standard",
"reverse_charge",
"vat_exempt"
]
},
"createdAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"type",
"number",
"name",
"contactName",
"email",
"address",
"postalCode",
"city",
"country",
"kvk",
"vatNumber",
"vatMode",
"createdAt"
],
"additionalProperties": false
}
Invoice · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"number": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Null until the invoice is finalized in the app or during dispatch."
},
"status": {
"type": "string",
"enum": [
"draft",
"final",
"sent",
"overdue",
"paid"
],
"description": "Derived display status. An unpaid draft with a past due date can be overdue."
},
"invoiceType": {
"type": "string",
"enum": [
"invoice",
"credit"
]
},
"clientId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"recipient": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"name",
"email"
],
"additionalProperties": false
},
"issueDate": {
"anyOf": [
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
},
{
"type": "null"
}
]
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
},
{
"type": "null"
}
]
},
"paidAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"scheduledSendAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"currency": {
"type": "string"
},
"vatMode": {
"type": "string",
"enum": [
"standard",
"reverse_charge",
"vat_exempt"
]
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"subtotalCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"vatCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"totalCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"createdAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"updatedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"number",
"status",
"invoiceType",
"clientId",
"recipient",
"issueDate",
"dueDate",
"paidAt",
"scheduledSendAt",
"currency",
"vatMode",
"notes",
"subtotalCents",
"vatCents",
"totalCents",
"createdAt",
"updatedAt"
],
"additionalProperties": false
}
InvoiceDetail · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"number": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Null until the invoice is finalized in the app or during dispatch."
},
"status": {
"type": "string",
"enum": [
"draft",
"final",
"sent",
"overdue",
"paid"
],
"description": "Derived display status. An unpaid draft with a past due date can be overdue."
},
"invoiceType": {
"type": "string",
"enum": [
"invoice",
"credit"
]
},
"clientId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"recipient": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"name",
"email"
],
"additionalProperties": false
},
"issueDate": {
"anyOf": [
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
},
{
"type": "null"
}
]
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
},
{
"type": "null"
}
]
},
"paidAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"scheduledSendAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"currency": {
"type": "string"
},
"vatMode": {
"type": "string",
"enum": [
"standard",
"reverse_charge",
"vat_exempt"
]
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"subtotalCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"vatCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"totalCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"createdAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"updatedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"lines": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"position": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"description": {
"type": "string"
},
"details": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"quantity": {
"type": "number"
},
"unit": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"unitPriceCents": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "May be negative on an existing credit invoice."
},
"vatRate": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"discountPct": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"periodStart": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"periodEnd": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"position",
"description",
"details",
"quantity",
"unit",
"unitPriceCents",
"vatRate",
"discountPct",
"periodStart",
"periodEnd"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"number",
"status",
"invoiceType",
"clientId",
"recipient",
"issueDate",
"dueDate",
"paidAt",
"scheduledSendAt",
"currency",
"vatMode",
"notes",
"subtotalCents",
"vatCents",
"totalCents",
"createdAt",
"updatedAt",
"lines"
],
"additionalProperties": false
}
Error · JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"error": {
"type": "string"
},
"issues": {
"description": "Validation messages grouped by top-level field, for example lines or clientId.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"required": [
"error"
],
"additionalProperties": false
}