Invoices

Create, send, and manage invoices through the admin API.

For general conventions, see JSON-API Conventions. For authentication, see Authentication.


Prerequisites

  • Bearer token with admin access + ?o={organization_id}

Invoice Actions

Cancel an Invoice

POST /api/v1/invoices/{invoice_id}/cancel?o={org_id}

Creates a credit note and voids the invoice.

Mark as Paid

Mark a SENT invoice as paid without processing a payment:

PUT /api/v1/invoices/{invoice_id}/set-paid?o={org_id}

{
  "send_notification": true
}

Set send_notification to true to email the customer a payment confirmation.

Change Recipient

Transfers an invoice to a different customer. This cancels the original invoice and creates a new copy for the new recipient:

POST /api/v1/invoices/{invoice_id}/change-recipient?o={org_id}

{
  "customer_id": "new-customer-uuid"
}

Creating an Invoice

All examples below assume BASE_URL = https://{your-domain}/api/v1.

Step 1 — Find a Customer by Email

GET {BASE_URL}/customers?o={org_id}&filter[email]=jane@example.com&include=address

The email filter performs an exact (case-insensitive) match.

For a fuzzy name/email/phone search, use the search filter instead:

GET {BASE_URL}/customers?o={org_id}&filter[search]=jane&include=address

Filter by Custom Field

Custom fields are referenced by their UUID. Each filter entry requires three parts:

Key Description
f_id Custom field UUID
o Operator: =, <>, <, >, <=, >=, in
v Value to match
GET {BASE_URL}/customers?o={org_id}&filter[custom_fields][{FIELD_UUID}][f_id]={FIELD_UUID}&filter[custom_fields][{FIELD_UUID}][o]==&filter[custom_fields][{FIELD_UUID}][v]=Premium

For checkbox fields, pass true / false as the value. For multi-select fields, the in operator accepts comma-separated values.

Step 2 — Create a Customer (if not found)

POST {BASE_URL}/customers?o={org_id}
{
  "data": {
    "type": "customers",
    "attributes": {
      "given_name": "Jane",
      "family_name": "Doe",
      "email": "jane@example.com",
      "company": "Acme Inc",
      "locale": "de",
      "mobile": "+4917612345678"
    }
  }
}

Required: email, locale

Create the customer's address as a separate resource:

POST {BASE_URL}/addresses?o={org_id}
{
  "data": {
    "type": "addresses",
    "attributes": {
      "name": "Jane Doe",
      "street_address": "Hauptstraße 42",
      "zip_code": "50667",
      "city": "Köln",
      "country_code": "DE"
    },
    "relationships": {
      "addressable": {
        "data": { "type": "customers", "id": "{customer_id}" }
      }
    }
  }
}

Step 3 — Create the Invoice (draft)

POST {BASE_URL}/invoices?o={org_id}
{
  "data": {
    "type": "invoices",
    "attributes": {
      "currency": "EUR",
      "recipient": "Acme Inc\nJane Doe\nHauptstraße 42\n50667 Köln",
      "calculate_from_net": true
    },
    "relationships": {
      "customer": {
        "data": { "type": "customers", "id": "{customer_id}" }
      }
    }
  }
}

Customer linking

Method When to use
relationships.customer in the request body Manual invoices — pass the customer ID directly
relationships.reference (order, subscription, booking pass) Backend derives customer from the referenced entity
Neither Create without a customer; link one later via PATCH

When the invoice is sent, the backend snapshots recipient and buyer_data from the customer so the invoice stays valid even if the profile changes later.

Attributes

Attribute Type Required Notes
currency string yes e.g. "EUR", "USD"
recipient string no Free-text address block; auto-filled from customer on send if blank
invoicing_party string no Your company's address block
calculate_from_net boolean no Default false. Set true when entering net amounts
issued_at date no Required when status ≠ draft
due_date date no
notes string no Appears on the invoice
vat_number string no
is_quote boolean no Set true to create a quote instead
number string no Must be unique within org if provided

Step 4 — Add Line Items

POST {BASE_URL}/items?o={org_id}&calculate_from_net=true
{
  "data": {
    "type": "items",
    "attributes": {
      "title": "Consulting — March 2026",
      "quantity": 10,
      "single_net_amount": 150.00,
      "tax_rate": 19
    },
    "relationships": {
      "document": {
        "data": { "type": "invoices", "id": "{invoice_id}" }
      }
    }
  }
}
Attribute Type Required Notes
title string yes Max 185 chars
quantity integer yes
tax_rate number yes ≥ 0 (e.g. 19 for 19%)
single_net_amount number yes* Required when calculate_from_net=true
single_gross_amount number yes* Required when calculate_from_net=false
subtitle string no Max 185 chars

* Provide the amount matching the invoice direction. The backend derives the other automatically.

Repeat for each line item. Invoice totals recalculate after each item.

Step 5 — Send the Invoice

POST {BASE_URL}/invoices/{invoice_id}/send?o={org_id}

Transitions status to sent and delivers via the configured notification channel.

Step 6 — Mark as Paid (optional)

POST {BASE_URL}/invoices/{invoice_id}/set-paid?o={org_id}&sendNotification=true

Full Workflow Summary

1. GET  /customers?filter[email]=...&include=address     → Find customer
2. POST /customers                                        → Create customer (if new)
3. POST /addresses                                        → Create customer address (if new)
4. POST /invoices                                         → Create draft invoice
5. POST /items  (repeat per line item)                    → Add line items
6. POST /invoices/{id}/send                               → Send invoice
7. POST /invoices/{id}/set-paid                           → Mark as paid

All endpoints include ?o={organization_id} for tenant context.


Common Errors

Error Cause Solution
403 — unauthorized Missing permissions for invoice operations Ensure admin token has the required abilities
422 — invoice not in SENT status set-paid on draft or already-paid invoice Check invoice status first