---
title: "Generate a compliant e-invoice"
method: POST
path: "/api/v1/invoices"
---

# Generate a compliant e-invoice

`POST /api/v1/invoices`

Anonymous endpoint. Rate-limited per source IP. Returns 201 with a download URL.

## Headers

- `Idempotency-Key` string — Client-supplied key. Replaying the same key with identical inputs returns the cached invoice; replaying with different inputs returns 422 idempotency_key_mismatch.
- `X-Email-Verification-Token` string — Email-ownership token from POST /api/v1/scribo/email-verifications/{id}/redeem. Required for the first invoice per sender email — without it (or a scribo session cookie) an unverified sender receives 401 email_verification_required.

## Request body

- CreateInvoiceInput
  - `sender` object, required
    - `legal_name` string, required
    - `country_code` string, required
    - `address_line1` string, required
    - `address_line2` string
    - `postcode` string, required
    - `city` string, required
    - `tax_id` string, nullable
    - `contact_email` string, email, required
    - `contact_phone` string, nullable
    - `contact_name` string, nullable
    - `tax_registration_id` string, nullable
  - `recipient` object, required
    - `legal_name` string, required
    - `country_code` string, required
    - `address_line1` string, required
    - `address_line2` string
    - `postcode` string, required
    - `city` string, required
    - `tax_id` string, nullable
    - `contact_email` string, email, required
    - `leitweg_id` string, nullable
  - `line_items` object[], required
    - `description` string, required
    - `quantity` string, required
    - `unit_code` string, nullable
    - `unit_price` string, required
    - `tax_rate` string, required
    - `tax_category_code` 'S' | 'Z' | 'E' | 'AE' | 'K' | 'G' | 'O', required
    - `discount` union
      - object
        - `type` 'amount', required
        - `value` string, required
        - `reason` string, required
      - object
        - `type` 'percent', required
        - `value` string, required
        - `reason` string, required
    - `tax_exemption_code` string, nullable
    - `tax_exemption_reason` string, nullable
  - `currency` string, required
  - `jurisdiction` string, nullable
  - `format_override` 'zugferd_comfort' | 'zugferd_basic' | 'xrechnung_cii' | 'xrechnung_ubl' | 'plain_pdf'
  - `notes` string
  - `invoice_number` string, nullable — Optional invoice number printed on the document (BT-1). Omit the field entirely to have the server assign one — do NOT send a placeholder like "auto", "TBD", or "N/A" (these are ignored and a real code is assigned). When present: 1-64 chars of letters, digits, space, or - / . # _.
  - `issue_date` string — Optional ISO date (BT-2) — the invoice's issue date. Defaults to today when omitted.
  - `due_date` string
  - `payment_terms` string, nullable
  - `payment_means` object — BG-16 payment instructions for a credit transfer. Provide EXACTLY ONE of: a SEPA `iban`, OR US domestic details (`account_number` + 9-digit ABA `routing_number`). An optional `bic` (SWIFT) may accompany either form — US accounts carry a SWIFT/BIC for inbound international wires. An optional free-text `bank` (beneficiary bank name + address) may also be included; it's shown on the invoice but not validated structurally. An object carrying no account details at all is treated as absent; supplying both account forms, or only half of the US pair, is rejected. XRechnung (German B2G) requires the `iban` form (BR-DE-1).
    - `type` 'credit_transfer', required
    - `iban` string, nullable
    - `bic` string, nullable
    - `account_number` string, nullable
    - `routing_number` string, nullable
    - `account_name` string, nullable
    - `bank` string, nullable
  - `delivery_date` string
  - `delivery_period` object
    - `start` string, required
    - `end` string, required

## Response `201`

Invoice generated and persisted

- Invoice
  - `invoice_id` string, uuid, required
  - `document_id` string, uuid, required
  - `format` 'zugferd_comfort' | 'zugferd_basic' | 'xrechnung_cii' | 'xrechnung_ubl' | 'plain_pdf', required
  - `download_url` string, uri, required
  - `download_url_expires_at` string, date-time, required
  - `preview_url` string, uri
  - `preview_url_expires_at` string, date-time
  - `submission` object
    - `status` 'not_provided', required
    - `message` string, required
    - `manual_upload_hints` object[]
      - `label` string, required
      - `url` string, uri, required
      - `leitweg_prefix` string
    - `planned` string[]
  - `validator_summary` object, required
    - `valid` boolean, required
    - `validator` string, required
    - `errors` object[]
      - `path` string
      - `rule` string
      - `message` string, required
  - `magic_link_sent` boolean, required
  - `recipient_email_sent` boolean

## Other responses

- `400` — Invalid input or downstream validator rejection
- `401` — Sender email not verified yet (email_verification_required) — run the email-verification flow and retry with X-Email-Verification-Token
- `403` — Verified email does not match sender.contact_email (verification_email_mismatch), or tenant is temporarily soft-blocked after repeated hard-limit hits (tenant_soft_blocked)
- `422` — Idempotency-Key mismatch
- `429` — Rate limit exceeded

---

[API](https://skmtc.net/causaprima/apis/scribo-invoice-api.md) · [All operations](https://skmtc.net/causaprima/apis/scribo-invoice-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/causaprima/scribo-invoice-api/revisions/73eb15ab4d15/schema)
