# 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.

**Scopes:** any of `invoices:write`, `write:draft`, `write:ledger`

## Request

```bash
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
  }'
```

## Headers

- `Idempotency-Key` (string, optional): 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. (max 255 chars)

## Body (`application/json`)

- `contact_id` (string (uuid), required): ID of the contact for this invoice
- `issue_date` (string, required): Invoice issue date in YYYY-MM-DD format (YYYY-MM-DD)
- `due_date` (string, optional): Payment due date in YYYY-MM-DD format (YYYY-MM-DD)
- `currency` (string, optional): ISO 4217 currency code (default: DKK). A currency Numina has no exchange rate for returns 400. Default: `"DKK"`. (max 3 chars)
- `currency_rate` (number, optional): 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_number` (integer, optional): 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_code` (string, optional): 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.
- `items` (array of objects, required): Invoice line items (minimum 1 item required)
  - `description` (string, required): Line item description
  - `quantity` (number, required): Quantity
  - `unit_price` (number, required): Unit price (excl. VAT)
  - `sales_account_number` (integer, optional): Sales account for this line (from GET /v1/accounts). Omit to use the invoice's `sales_account_number`.
  - `vat_code` (string, optional, 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`.
- `memo` (string, optional): Note displayed on the invoice (max 2000 chars)
- `bank_account_id` (string (uuid), optional): 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.
- `book` (boolean, optional): 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`.

## Response `201`

Invoice created successfully

```json
{
  "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"
}
```

## Errors

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

See https://developers.numina.app/errors.md for the error format.
