> ## Documentation Index
> Fetch the complete documentation index at: https://docs.infinity.swiss/llms.txt
> Use this file to discover all available pages before exploring further.

# Create invoice

> Creates a new customer invoice in the organisation. Set `isDraft` to `false` to immediately post the invoice and create the underlying transactions.

## Creating a customer invoice

Invoices belong to a template and are addressed to a contact (or a free-text recipient). They are made up of an ordered list of positions, each of which has a discriminator `type`:

* `product` — a standard billable line with a `quantity`, `singleAmount`, and `contraAccount`
* `discount` — reduces the preceding positions, by either a `percentage` or an absolute `amount` (exactly one of the two must be set)
* `text` — a non-billable text line
* `subtotal` — renders the running subtotal of the preceding positions

### Draft vs. posted

The `isDraft` flag controls whether the new invoice is posted immediately:

* `isDraft: true` — saves the invoice as a draft. No accounting transactions are created.
* `isDraft: false` — posts the invoice and creates the underlying transactions on `openingDate`. The transaction date must fall within an editable fiscal year and VAT period.

### Foreign-currency invoices

To issue an invoice in a non-primary currency, provide both `originalCurrency` (3-character ISO 4217 code) and `exchangeRate`. The currency must already be enabled in the organisation, and the rate is expressed as `1 originalCurrency = X primaryCurrency` (e.g. `0.98` if 1 EUR = 0.98 CHF). The two fields must always be set together or omitted together.

### Example

The following payload creates a posted CHF invoice with two product lines and a 10% discount:

```json theme={null}
{
  "template": "6474bb0cccc5b63aae56fa15",
  "openingDate": "2026-05-12",
  "recipient": "6474bb0cccc5b63aae56fa20",
  "title": "Beratungsleistungen Mai 2026",
  "isDraft": false,
  "payableInDays": 30,
  "amountMode": "gross",
  "language": "de",
  "positions": [
    {
      "type": "product",
      "name": "Beratung",
      "quantity": 4,
      "unit": "h",
      "singleAmount": 18000,
      "contraAccount": 3200,
      "taxCode": "UN81"
    },
    {
      "type": "product",
      "name": "Workshop-Vorbereitung",
      "quantity": 1,
      "singleAmount": 25000,
      "contraAccount": 3200,
      "taxCode": "UN81"
    },
    {
      "type": "discount",
      "percentage": 10,
      "discountText": "Rabatt"
    }
  ]
}
```


## OpenAPI

````yaml POST /v1/invoices
openapi: 3.1.0
info:
  title: infinity.swiss Open API
  version: '1.0'
  summary: ''
servers:
  - url: https://api.infinity.swiss
security:
  - apiKey: []
paths:
  /v1/invoices:
    post:
      tags: []
      summary: Create invoice
      description: >-
        Creates a new customer invoice in the organisation. Set `isDraft` to
        `false` to immediately post the invoice and create the underlying
        transactions.
      operationId: create-invoice-v1
      requestBody:
        description: The invoice payload.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InvoiceInput'
      responses:
        '201':
          description: The invoice was created successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  invoice:
                    $ref: '#/components/schemas/Invoice'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '403':
          $ref: '#/components/responses/InvoicePostingForbidden'
        '404':
          $ref: '#/components/responses/InvoiceReferencedResourceNotFound'
components:
  schemas:
    InvoiceInput:
      title: InvoiceInput
      type: object
      description: Request body for creating a customer invoice.
      required:
        - template
        - openingDate
        - positions
        - isDraft
      allOf:
        - $ref: '#/components/schemas/InvoiceBaseFields'
        - type: object
          properties:
            isDraft:
              type: boolean
              description: >-
                When `true` the invoice is created in draft state and no
                transactions are posted. Set to `false` to immediately post the
                invoice.
            isQrInvoice:
              type: boolean
              description: >-
                When `true` the document is rendered as a QR-bill (Swiss
                QR-Rechnung). Defaults to `false`.
    Invoice:
      title: Invoice
      type: object
      description: The full representation of a customer invoice.
      allOf:
        - $ref: '#/components/schemas/InvoiceCommonFields'
        - type: object
          required:
            - isQrInvoice
            - totalAmountPaid
            - totalRestAmount
            - isPaid
            - isOverpaid
            - isAnnulled
          properties:
            isQrInvoice:
              type: boolean
              description: '`true` when the document is rendered as a Swiss QR-bill.'
            totalAmountPaid:
              type: integer
              description: Sum of all recorded payments in cents.
            totalRestAmount:
              type: integer
              description: Outstanding balance in cents.
            isPaid:
              type: boolean
              description: '`true` once the invoice is fully paid.'
            isOverpaid:
              type: boolean
            isAnnulled:
              type: boolean
              description: '`true` once the invoice has been annulled.'
    InvoiceBaseFields:
      title: InvoiceBaseFields
      type: object
      description: Fields shared between invoice and estimate request payloads.
      properties:
        template:
          type: string
          description: The id of the invoice template to render the document with.
        openingDate:
          type: string
          format: date
          description: >-
            The opening date (issue date) of the document in YYYY-MM-DD format.
            For invoices, this is also the date used for the posted accounting
            transactions.
        positions:
          type: array
          description: >-
            The ordered list of positions on the document. At least one position
            is required, and discount positions must specify exactly one of
            `percentage` or `amount`.
          items:
            $ref: '#/components/schemas/InvoicePosition'
        recipient:
          type: string
          description: >-
            The id of the contact this document is addressed to. Required unless
            `customRecipientText` is provided.
        customRecipientText:
          type: string
          maxLength: 2000
          description: >-
            An ad-hoc recipient text used when no contact is associated with the
            document.
        title:
          type: string
          maxLength: 128
          description: Optional document title displayed on the rendered output.
        payableInDays:
          type: integer
          minimum: 0
          maximum: 365
          description: >-
            Payment terms for this document in days from the opening date.
            Defaults to 30 on create.
        introductoryText:
          type: string
          maxLength: 2000
          description: Text rendered above the positions.
        closingText:
          type: string
          maxLength: 2000
          description: Text rendered below the positions.
        amountMode:
          $ref: '#/components/schemas/AmountMode'
        language:
          $ref: '#/components/schemas/InvoiceLanguage'
        columns:
          type: array
          description: >-
            The columns to render in the positions table. If not provided the
            template default is used.
          items:
            $ref: '#/components/schemas/InvoiceColumn'
        originalCurrency:
          type: string
          minLength: 3
          maxLength: 3
          description: >-
            For foreign-currency documents: the 3-character ISO 4217 currency
            code the document is denominated in. Must be provided together with
            `exchangeRate`. Must be a currency code that is enabled in the
            organisation.
        exchangeRate:
          type: number
          exclusiveMinimum: 0
          description: >-
            For foreign-currency documents: the conversion rate from the
            original currency to the primary accounting currency, expressed as
            `1 originalCurrency = X primaryCurrency`. Must be provided together
            with `originalCurrency`.
    InvoiceCommonFields:
      title: InvoiceCommonFields
      type: object
      description: Response fields shared between invoices and estimates.
      required:
        - id
        - isDraft
        - number
        - organisation
        - template
        - openingDate
        - dueDate
        - positions
        - totalAmount
        - totalNetAmount
        - totalVatDebt
      properties:
        id:
          type: string
          description: A unique hex identifier for the document.
          readOnly: true
        isDraft:
          type: boolean
        number:
          type: string
          description: >-
            The formatted document number. May be empty for drafts that have not
            yet been issued.
        organisation:
          type: string
          description: The id of the owning organisation.
          readOnly: true
        template:
          type: string
          description: The id of the template used to render the document.
        recipient:
          type: string
          description: The id of the contact this document is addressed to.
        customRecipientText:
          type: string
        title:
          type: string
        openingDate:
          type: string
          format: date
        dueDate:
          type: string
          format: date
        payableInDays:
          type: integer
        introductoryText:
          type: string
        closingText:
          type: string
        positions:
          type: array
          items:
            $ref: '#/components/schemas/InvoicePosition'
        totalAmount:
          type: integer
          description: Total gross amount in cents (e.g. 12030 = 120.30).
        totalNetAmount:
          type: integer
          description: Total net amount in cents.
        totalVatDebt:
          type: integer
          description: Total VAT debt in cents.
        amountMode:
          $ref: '#/components/schemas/AmountMode'
        language:
          $ref: '#/components/schemas/InvoiceLanguage'
        originalCurrency:
          type: string
          minLength: 3
          maxLength: 3
          description: >-
            ISO 4217 currency code for the original document currency. Omitted
            for documents in the primary accounting currency.
        exchangeRate:
          type: number
          description: >-
            Exchange rate from the original currency to the primary accounting
            currency, if applicable.
        columns:
          type: array
          items:
            $ref: '#/components/schemas/InvoiceColumn'
        emailDeliveries:
          type: array
          description: Log of any email delivery attempts for this document.
          items:
            $ref: '#/components/schemas/EmailDelivery'
        publicPdfUrl:
          type: string
          description: >-
            A public URL for downloading the rendered PDF without
            authentication. May be omitted while the URL is being generated.
    InvoicePosition:
      title: InvoicePosition
      description: >-
        A single position on an invoice or estimate. Discriminated by the `type`
        field.
      oneOf:
        - $ref: '#/components/schemas/ProductPosition'
        - $ref: '#/components/schemas/DiscountPosition'
        - $ref: '#/components/schemas/TextPosition'
        - $ref: '#/components/schemas/SubtotalPosition'
      discriminator:
        propertyName: type
        mapping:
          product:
            $ref: '#/components/schemas/ProductPosition'
          discount:
            $ref: '#/components/schemas/DiscountPosition'
          text:
            $ref: '#/components/schemas/TextPosition'
          subtotal:
            $ref: '#/components/schemas/SubtotalPosition'
    AmountMode:
      title: AmountMode
      type: string
      description: >-
        Whether position amounts are entered gross (including VAT) or net
        (excluding VAT).
      enum:
        - gross
        - net
    InvoiceLanguage:
      title: InvoiceLanguage
      type: string
      description: The language used to render the document.
      enum:
        - de
        - en
        - fr
        - it
    InvoiceColumn:
      title: InvoiceColumn
      description: A column that can be shown on the rendered invoice or estimate.
      type: string
      enum:
        - quantity
        - unit
        - articleNumber
        - name
        - date
        - vatDebt
        - vatRate
        - singleAmount
        - totalAmount
        - discountRate
        - positionNumber
        - customTextColumn1
        - customTextColumn2
        - customTextColumn3
    EmailDelivery:
      title: EmailDelivery
      type: object
      description: A record of an attempt to send the document by email.
      required:
        - recipientEmail
        - status
        - timestamp
      properties:
        recipientEmail:
          type: string
          format: email
          description: The address the document was sent to.
        status:
          type: string
          description: The delivery status reported by the email provider.
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the delivery was recorded.
    ProductPosition:
      title: ProductPosition
      type: object
      description: >-
        A standard billable line item with a quantity, single amount, and contra
        account. Most invoice and estimate positions are of this type.
      required:
        - type
        - name
        - singleAmount
        - quantity
        - contraAccount
      properties:
        type:
          type: string
          enum:
            - product
          description: Discriminator for the position type.
        id:
          type: string
          description: >-
            Server-assigned identifier for the position. Present on responses;
            omit when creating.
          readOnly: true
        name:
          type: string
          maxLength: 256
          description: The name of the product or service for this position.
        singleAmount:
          type: integer
          description: >-
            The unit amount in cents. May be expressed gross or net depending on
            the document's `amountMode`.
          example: 12000
        quantity:
          type: number
          description: The number of units billed for this position.
          example: 2
        contraAccount:
          type: integer
          minimum: 1000
          maximum: 9999
          description: >-
            The contra account number this position books against (typically a
            revenue account).
        unit:
          type: string
          maxLength: 4
          description: Optional unit, e.g. `h`, `kg`, `pcs`.
        taxCode:
          $ref: '#/components/schemas/TaxCode'
        articleNumber:
          type: string
          maxLength: 48
          description: Optional article or SKU number to display alongside the position.
        description:
          type: string
          maxLength: 2000
          description: Optional multi-line description of the position.
        date:
          type: string
          maxLength: 12
          description: Optional free-text date column for the position, e.g. service date.
        discountRate:
          type: number
          minimum: 0
          maximum: 100
          description: Optional per-position discount rate in percent.
        customTextColumns:
          $ref: '#/components/schemas/CustomTextColumns'
        totalAmount:
          type: integer
          readOnly: true
          description: >-
            The total amount of this position (gross), in cents. Computed by the
            server.
        vatDebt:
          type: integer
          readOnly: true
          description: The VAT debt for this position, in cents. Computed by the server.
        originalCurrency:
          type: string
          minLength: 3
          maxLength: 3
          readOnly: true
          description: >-
            The 3-character ISO 4217 currency code in which this position was
            originally entered, if different from the primary accounting
            currency.
        exchangeRate:
          type: number
          readOnly: true
          description: The exchange rate used to convert the original currency amount.
    DiscountPosition:
      title: DiscountPosition
      type: object
      description: >-
        A discount line that reduces the sum of the preceding positions either
        by a percentage or by an absolute amount.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - discount
        id:
          type: string
          readOnly: true
        percentage:
          type: number
          minimum: 0
          maximum: 100
          description: >-
            A percentage discount applied to the preceding positions. Exactly
            one of `percentage` or `amount` must be set.
        amount:
          type: integer
          minimum: 0
          description: >-
            An absolute discount amount in cents. Exactly one of `percentage` or
            `amount` must be set.
        discountText:
          type: string
          maxLength: 256
          description: Optional display text for the discount, e.g. "Rabatt".
    TextPosition:
      title: TextPosition
      type: object
      description: >-
        A non-billable text line that is rendered between positions for
        organisation or commentary.
      required:
        - type
        - content
      properties:
        type:
          type: string
          enum:
            - text
        id:
          type: string
          readOnly: true
        content:
          type: string
          maxLength: 2000
          description: The text content to render.
        style:
          type: string
          enum:
            - normal
            - headline
            - fine-print
          description: The visual style of the text line.
    SubtotalPosition:
      title: SubtotalPosition
      type: object
      description: >-
        A line that renders the running subtotal of all preceding positions,
        optionally with a custom label.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - subtotal
        id:
          type: string
          readOnly: true
        label:
          type: string
          maxLength: 128
          description: Optional label for the subtotal line.
    TaxCode:
      title: TaxCode
      description: >-
        A VAT tax code. See [Liste der
        MWST-Codes](https://docs.infinity.swiss/de/docs/accounting/vat/tax-codes)
        for details. The empty string indicates no VAT.
      oneOf:
        - type: string
          enum:
            - ''
        - type: string
          enum:
            - UN81
            - UN77
            - UR26
            - UR25
            - US38
            - US37
            - UO81
            - UO77
            - BZB81
            - BZB77
            - BZM81
            - BZM77
            - VM81
            - VM38
            - VM26
            - VB81
            - VB38
            - VB26
            - VM77
            - VM37
            - VM25
            - VB77
            - VB37
            - VB25
            - ES81
            - ES26
            - ES77
            - ES25
            - UNO
            - UEX
            - ULA
            - ZOLLM
            - ZOLLB
    CustomTextColumns:
      title: CustomTextColumns
      type: object
      description: Optional values for the three custom text columns on a product position.
      properties:
        customTextColumn1:
          type: string
          maxLength: 64
        customTextColumn2:
          type: string
          maxLength: 64
        customTextColumn3:
          type: string
          maxLength: 64
  responses:
    BadRequest:
      description: >-
        A bad request is thrown if the request payload is malformed or fails
        validation.
      content:
        application/json:
          schema:
            type: object
            required:
              - code
            properties:
              code:
                type: string
                description: |-
                  The specific error code for this request. May be:
                  - `general/missing-fields`
                  - `general/malformed-request`
                  - `general/invalid-date`
                  - `general/input-too-long`
                  - `general/zero-or-negative-amount-not-allowed`
              invalidFields:
                type: array
                description: A list of the fields which were invalid, if applicable.
                items:
                  type: string
    Unauthorised:
      description: >-
        An unauthorised request was submitted. You may be using an incorrect or
        expired API token, or are attempting to access a document outside of the
        scope of the token.
      content:
        application/json:
          schema:
            type: object
            properties:
              code:
                type: string
                x-stoplight:
                  id: 08hkw8ukmi2lp
                description: Specific error code
          examples:
            Unauthorised request:
              value:
                code: general/unauthorised
    InvoicePostingForbidden:
      description: >-
        The requested transition is not allowed. This may happen when the
        relevant fiscal year or VAT period is not editable, or when the invoice
        is in a state that does not permit the operation.
      content:
        application/json:
          schema:
            type: object
            properties:
              code:
                type: string
                description: |-
                  The specific error code for this request. May include:
                  - `fiscal-year/not-found`
                  - `fiscal-year/closed`
                  - `vat-period/closed`
                  - `customer-invoice/is-draft`
                  - `customer-invoice/is-annulled`
                  - `customer-invoice/cannot-annul`
                  - `customer-invoice/cannot-modify`
    InvoiceReferencedResourceNotFound:
      description: >-
        A referenced resource could not be found, e.g. the invoice template,
        recipient contact, or one of the contra accounts used by a position.
      content:
        application/json:
          schema:
            type: object
            properties:
              code:
                type: string
                description: |-
                  The specific error code for this request. May include:
                  - `template/not-found`
                  - `contact/not-found`
                  - `ledger/account-not-found`
  securitySchemes:
    apiKey:
      name: x-api-token
      type: apiKey
      in: header
      description: >-
        API token for authentication. Obtain from your Infinity account
        settings.

````