NuminaDevelopers
Dashboard
Create invoice

Create invoice

POST
/v1/invoices

Creates a sales invoice draft. Each line takes its VAT code and sales account from the item, or from the invoice's vat_code and sales_account_number when the item has none, so one invoice can mix rates (for example a 25% item and a fee without VAT). Line sales accounts must be marked as sales accounts and line VAT codes must be sales codes.

With book: true the request also needs invoices:book and books the invoice the same way as POST /v1/invoices/{id}/book. Each call is your own approval of the invoice: Numina does not review it, so make sure a person at your company has checked it or has approved the rules your system books by. Booking is final and cannot be undone through this API. It does not send the invoice. Fuld Service customers get 403 booking_not_available, a date in a closed accounting year or before the first accounting date gets 400, and an invoice with no bank account to be paid into gets 400 payment_details_required, all before anything is written.

Payment details come from bank_account_id, or from the company's default account when you omit it (see is_invoice_default on GET /v1/bank-accounts). Numina never guesses between several accounts: with more than one and no default, pass bank_account_id.

If booking fails after those checks (400, 409 or 500), the draft remains, and the error response does not include its id. Look for it with GET /v1/invoices?status=draft before you retry, because a failed request frees its Idempotency-Key and a retry creates a second draft. You can also create without book and then call POST /v1/invoices/{id}/book, which is safe to repeat.

Send an Idempotency-Key header (for example a UUID generated per request) to retry safely after a timeout or network error. A retry with the same key and the same body returns the original response with the header Idempotent-Replayed: true. Keys are scoped to the company and kept for 24 hours after a successful request. A failed request (4xx/5xx) frees the key, so a retry with that key runs again. Reusing a key with a different body returns 422; retrying while the first request is still processing returns 409.

Requires one ofinvoices:writewrite:draftwrite:ledger

Headers

  • Idempotency-Keystring· max 255 chars
    Unique key for this request (max 255 characters). Retrying with the same key and body returns the original response instead of doing the work again.

Body

application/json

  • contact_idstring (uuid)Required
    ID of the contact for this invoice
  • issue_datestringRequired· YYYY-MM-DD
    Invoice issue date in YYYY-MM-DD format
  • due_datestring· YYYY-MM-DD
    Payment due date in YYYY-MM-DD format
  • currencystring· max 3 chars
    ISO 4217 currency code (default: DKK). A currency Numina has no exchange rate for returns 400.

    Default "DKK"

  • Exchange rate to DKK (1.0 for DKK invoices), kept while the invoice is a draft. Ignored at booking: a foreign-currency invoice then uses the latest rate Numina has on or before its issue date. Without such a rate Numina uses a fixed fallback rate.

    Default 1

  • Sales account for lines that don't set their own (from GET /v1/accounts). Required unless every item has its own sales_account_number.
  • vat_codestring
    VAT code for lines that don't set their own (e.g. 'U25'). Omit for 0% VAT on those lines. The rate comes from the code; GET /v1/vat-codes lists them. Set vat_code on each item to mix rates on one invoice.
  • itemsarray of objectsRequired
    Invoice line items (minimum 1 item required)
    +Show 5 child attributes
    • descriptionstringRequired
      Line item description
    • quantitynumberRequired
      Quantity
    • unit_pricenumberRequired
      Unit price (excl. VAT)
    • Sales account for this line (from GET /v1/accounts). Omit to use the invoice's sales_account_number.
    • vat_codestring· nullable
      VAT code for this line (from GET /v1/vat-codes). null for no VAT on this line. Omitted, the line takes the default VAT code of its own sales_account_number, as in the invoice editor, and otherwise the invoice's vat_code.
  • memostring· max 2000 chars
    Note displayed on the invoice
  • bank_account_idstring (uuid)
    The bank account the customer pays into (from GET /v1/bank-accounts, one with usable_on_invoices). Its payment details are copied onto the invoice. Omit to use the account marked is_invoice_default; when there is none the draft has no payment details and cannot be booked.
  • bookboolean
    If true, also requires invoices:book and books the invoice (assigns invoice number, creates AR entries, status becomes 'sent'). It does not send it. If false, saves a draft.

    Default false

Errors

  • 400Invalid input or validation failed
  • 401Invalid or missing authentication
  • 403Missing scope, the company is archived, or the company is on Fuld Service
  • 409The invoice is being updated (with `book: true`; the draft remains), or a request with the same Idempotency-Key is still being processed
  • 422The Idempotency-Key was already used with a different request
  • 429Rate limit exceeded
  • 500Internal Server Error

Every error has the same shape. See Errors.

cURL
curl -X POST https://api.numina.app/v1/invoices \
  -H "Authorization: Bearer numi_YOUR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "550e8400-e29b-41d4-a716-446655440000",
    "issue_date": "2026-01-15",
    "due_date": "2026-02-15",
    "currency": "DKK",
    "currency_rate": 1,
    "sales_account_number": 1000,
    "vat_code": "U25",
    "items": [
      {
        "description": "Consulting services",
        "quantity": 10,
        "unit_price": 1000,
        "sales_account_number": 1030,
        "vat_code": "U25"
      }
    ],
    "memo": "Payment due within 30 days",
    "bank_account_id": "550e8400-e29b-41d4-a716-446655440000",
    "book": false
  }'
JavaScript
const response = await fetch("https://api.numina.app/v1/invoices", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NUMINA_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "contact_id": "550e8400-e29b-41d4-a716-446655440000",
    "issue_date": "2026-01-15",
    "due_date": "2026-02-15",
    "currency": "DKK",
    "currency_rate": 1,
    "sales_account_number": 1000,
    "vat_code": "U25",
    "items": [
      {
        "description": "Consulting services",
        "quantity": 10,
        "unit_price": 1000,
        "sales_account_number": 1030,
        "vat_code": "U25"
      }
    ],
    "memo": "Payment due within 30 days",
    "bank_account_id": "550e8400-e29b-41d4-a716-446655440000",
    "book": false
  }),
});

const data = await response.json();
Python
import uuid
import os

import requests

response = requests.post(
    "https://api.numina.app/v1/invoices",
    headers={
        "Authorization": f"Bearer {os.environ['NUMINA_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "contact_id": "550e8400-e29b-41d4-a716-446655440000",
        "issue_date": "2026-01-15",
        "due_date": "2026-02-15",
        "currency": "DKK",
        "currency_rate": 1,
        "sales_account_number": 1000,
        "vat_code": "U25",
        "items": [
            {
                "description": "Consulting services",
                "quantity": 10,
                "unit_price": 1000,
                "sales_account_number": 1030,
                "vat_code": "U25"
            }
        ],
        "memo": "Payment due within 30 days",
        "bank_account_id": "550e8400-e29b-41d4-a716-446655440000",
        "book": False
    },
)
data = response.json()
Response
201
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "invoice_number": 1001,
  "type": "invoice",
  "credited_invoice_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "sent",
  "issue_date": "2026-01-15",
  "due_date": "2026-02-15",
  "contact_id": "550e8400-e29b-41d4-a716-446655440001",
  "contact_name": "Acme ApS",
  "currency": "DKK",
  "subtotal": 10000,
  "vat": 2500,
  "vat_rate": 25,
  "total": 12500,
  "has_mixed_vat": false,
  "outstanding_balance": 12500,
  "outstanding_balance_currency": "DKK",
  "currency_rate": 0,
  "delivery_status": "sent",
  "email_delivery_status": "string",
  "email_sent_at": "string",
  "e_invoice_sent_at": "string",
  "is_overdue": false,
  "payment_state": "open",
  "unmatched_payments_note": "string",
  "items": [
    {
      "unit": "string",
      "vat_account_id": "string",
      "vat_code": "string",
      "sales_account_number": 0,
      "vat_rate": 0,
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "description": "Consulting services",
      "quantity": 10,
      "unit_price": 1000
    }
  ],
  "memo": "Project Q1 delivery",
  "payment_details": {
    "bank_name": "Danske Bank",
    "scheme": "dk-registration-number",
    "clearing_code": "3409",
    "account_number": "0012345678",
    "iban": "DK5030000012345678",
    "swift_bic": "DABADKKK"
  },
  "created_at": "2026-01-15T10:30:00Z"
}