Invoices

Outgoing invoices and credit notes, with their line items and lifecycle. Each invoice is returned with its embedded client summary and line items.

These pages show the shape; the OpenAPI spec has every field, constraint, and error.

Operations

Method & path Ability
GET /api/v1/invoices invoices:read
GET /api/v1/invoices/{invoice} invoices:read
GET /api/v1/invoices/{invoice}/pdf invoices:read
POST /api/v1/invoices invoices:write
PATCH /api/v1/invoices/{invoice} invoices:write
DELETE /api/v1/invoices/{invoice} invoices:write
POST /api/v1/invoices/{invoice}/credit-note invoices:write
POST /api/v1/invoices/{invoice}/send invoices:write
POST /api/v1/invoices/{invoice}/mark-sent invoices:write
POST /api/v1/invoices/{invoice}/mark-paid invoices:write
POST /api/v1/invoices/{invoice}/revert-to-draft invoices:write

Create an invoice

POST /api/v1/invoices creates a draft. Required: client_id, currency, invoice_date, due_date, and at least one line in items. Each line needs description, quantity, unit_price, and a btw_percentage of 0, 9, or 21. invoice_number is optional — omit it and the next number is allocated automatically. As a create, it takes an Idempotency-Key.

curl -X POST https://billey.nl/api/v1/invoices \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": 7,
    "currency": "EUR",
    "invoice_date": "2026-07-21",
    "due_date": "2026-08-04",
    "notes": "Thanks for your business.",
    "items": [
      { "description": "Consulting", "quantity": "10", "unit_price": "103.31", "btw_percentage": 21 }
    ]
  }'
{
  "data": {
    "id": 42,
    "invoice_number": "2026-0042",
    "status": "draft",
    "is_credit_note": false,
    "currency": "EUR",
    "invoice_date": "2026-07-21",
    "due_date": "2026-08-04",
    "subtotal": "1033.10",
    "btw_total": "217.05",
    "total": "1250.15",
    "reverse_charge_applied": false,
    "tax_treatment": "standard",
    "client": { "id": 7, "name": "Example Client B.V." },
    "line_items": [
      {
        "id": 1,
        "description": "Consulting",
        "quantity": "10.00",
        "unit_price": "103.31",
        "btw_percentage": "21.00",
        "btw_amount": "217.05",
        "line_total": "1033.10"
      }
    ]
  }
}

You can optionally pass time_entry_ids on create to claim uninvoiced time entries onto the new invoice.

Download the PDF

GET /api/v1/invoices/{invoice}/pdf renders and returns the same PDF the web app's "Download PDF" button produces — same branding, template, and payment QR code (not JSON).

curl -L https://billey.nl/api/v1/invoices/42/pdf \
  -H "Authorization: Bearer $TOKEN" \
  -o invoice.pdf

This route carries its own, tighter rate limit — see Rate limits.

Cross-border VAT (tax_treatment)

An invoice carries a tax_treatment that controls its VAT treatment:

  • standard — normal Dutch BTW (the default).
  • reverse_charge_eu — EU B2B reverse charge (btw verlegd).
  • out_of_scope_non_eu_service — a service to a non-EU business, outside the scope of Dutch BTW.
  • export_non_eu_goods — a 0% export of physical goods to a destination outside the EU (rubriek 3a). Goods lines only — a line classified as a service is rejected. Issuing creates a pending export-evidence record; the BTW filing stays blocked until the evidence (customs export declaration, transport documents) is verified in the Billey UI.

Send it on create or update; when omitted it defaults to standard. The response also includes reverse_charge_applied, a derived compatibility boolean that is true exactly when tax_treatment is reverse_charge_eu — new integrations should read tax_treatment. Quotes carry the same field.

Update an invoice

PATCH /api/v1/invoices/{invoice} is a full replace, not a partial merge — resend every header field and the complete items array, and only while the invoice is a draft. This is the single most common integration mistake; read PATCH behaviour first.

Lifecycle transitions

Move an invoice through its states with these bodyless POSTs (no Idempotency-Key); an invalid transition returns 409:

  • send — email the invoice to the client and mark it Sent (the client must have an email address, else 422).
  • mark-sent — mark Sent without emailing, for when you deliver it through your own channel.
  • mark-paid — transition Sent → Paid.
  • revert-to-draft — Sent → Draft; a paid invoice, or one in a locked period, cannot be reverted (409).

Credit notes

POST /api/v1/invoices/{invoice}/credit-note creates a credit note against a sent invoice (which must not itself be a credit note). Credit-note totals are stored positive; the direction is carried by is_credit_note: true. It is a create, so it takes an Idempotency-Key.

Delete

DELETE /api/v1/invoices/{invoice} soft-deletes a draft invoice (409 otherwise) and returns 204.

Something inaccurate or missing? support@billey.nl