---
title: "Create a draft invoice from a worksheet"
method: POST
path: "/v1/invoices"
tags: ["Invoices"]
---

# Create a draft invoice from a worksheet

`POST /v1/invoices`

## Request

- Base URL: `https://api.officina.it`
- URL: `https://api.officina.it/v1/invoices`
- Auth: HTTP bearer

Snapshots the referenced worksheet's totals and line items
into a new draft invoice. The worksheet MUST belong to the
authenticated user's company; cross-tenant requests return
404 (same as direct invoice access).

`payment_due_at` is optional. When omitted the server
computes today + the company's default payment term (e.g.
`30_days`), matching the web flow.

The response includes the materialized `line_items` array.

## Request body

- InvoiceCreateRequest: Body of `POST /v1/invoices`. A mobile invoice is always created from an existing worksheet — there is no "blank invoice + add lines" flow on this tier.
  - `worksheet_id` string, required: Source worksheet UUID (must belong to caller's company).
  - `payment_method` string, required: Free-form payment method label.
  - `payment_due_at` string, date-time, nullable: Optional. RFC 3339 between 1900-01-01 and 2100-12-31 (see `WritableTimestampNullable`). Defaults to today + the company's configured payment term.

## Response `201`

Invoice created.

- Invoice: A workshop invoice. Multi-tenant: belongs to one company. Created from a worksheet; transitions draft → issued via `POST /v1/invoices/{id}/issue`.
  - `object` 'invoice', required
  - `id` string, required
  - `status` 'draft' | 'issued', required: Derived from `issued_at`. Soft-deleted invoices 404 before reaching the client — "deleted" is never emitted.
  - `title` string, nullable
  - `comments` string, nullable
  - `internal_comments` string, nullable
  - `payment_method` string, required
  - `payment_condition` string, required
  - `payment_due_at` string, date-time, required
  - `vat_exigibility` string, required
  - `sezionale_id` string, nullable
  - `pa_document_type` string, nullable
  - `pa_document_number` string, nullable
  - `pa_document_date` string, date-time, nullable
  - `pa_cig` string, nullable
  - `pa_cup` string, nullable
  - `customer_id` string, required
  - `vehicle_id` string, nullable
  - `worksheet_id` string, nullable
  - `seq_number` integer, required: Per-company monotonic invoice number. 0 on drafts; assigned atomically at issuance.
  - `total_before_tax` number, double, required: Money. Float64 — clients render via the workshop locale formatter; avoid arithmetic.
  - `total_tax` number, double, required
  - `total_after_tax` number, double, required
  - `total_discount` number, double, required
  - `total_after_discount` number, double, required
  - `total_payments` number, double, required
  - `outstanding_payments` number, double, required
  - `customer_name` string, required
  - `vehicle_name` string, nullable
  - `vehicle_license` string, nullable
  - `created_by_full_name` string, required
  - `sezionale_code` string, nullable
  - `work_category_name` string, nullable
  - `work_category_names` string[], required: Names of the linked worksheet's work categories, in order. `[]` when the invoice has no worksheet or the worksheet has none. `work_category_name` is the first one. Available since revision 2026-10-03.
  - `sdi_status` string, required: Italian SDI e-invoice lifecycle status. Empty string when no e-invoice exists for this invoice. Possible non-empty values: "sent", "rejected", "pending_normal", "pending_warning", "pending_late".
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `issued_at` string, date-time, nullable: Populated once the invoice is issued.
  - `deleted_at` string, date-time, nullable: Soft-deletion timestamp. Populated only on the response from DELETE /v1/invoices/{id}.
  - `line_items` InvoiceLineItem[]: Populated on detail and post-mutation responses. Omitted from list rows to keep payloads small.
    - `object` 'invoice_line_item', required
    - `id` string, required
    - `line_type` 'item' | 'note' | 'separator', required: `item` is a priced line. `note` is a description-only line and `separator` an empty spacer row: both print without quantity, price and VAT and carry zero amounts (ignore their numeric fields). Lines from documents without types report `item`.
    - `article_id` string, nullable
    - `description` string, required
    - `quantity` number, double, required
    - `unit_type` string, required
    - `unit_price` number, double, required
    - `unit_cost` number, double, nullable
    - `unit_discount_rate` number, double, required
    - `tax_rate` number, double, required
    - `vat_code` string, nullable
    - `vat_description` string, nullable
    - `total_price` number, double, required
    - `total_before_tax` number, double, required
    - `total_tax` number, double, required
    - `total_after_tax` number, double, required
    - `created_at` string, date-time, required

## Other responses

- `400`: Request body is unparseable or X-API-Version is missing.
- `401`: Missing, malformed, expired, or wrong-audience bearer token.
- `402`: The company has no active subscription, or its plan does not include API access. API access is included in the Business and Illimitato plans.
- `403`: The authenticated user lacks the required permission.
- `404`: Resource does not exist OR belongs to a different company. The two cases are intentionally indistinguishable.
- `409`: The request conflicts with the current state of the resource. Examples: issuing an invoice that has already been issued; back-dating an issuance that would violate the monotonic per-company sequence; deleting a credit note whose SDI electronic counterpart has already been transmitted and cannot be retracted; mutating a worksheet whose linked invoice has been issued (the worksheet is frozen — its totals are now part of the issued invoice's accounting record and must not desync).
- `415`: Content-Type is not `application/json`, or Accept rejects JSON.
- `422`: A request field is missing or invalid (fast-fail).
- `500`: Unhandled server error. Detail is logged, not exposed.

---

[API](https://skmtc.dev/officina/apis/officina-it-api.md) · [All operations](https://skmtc.dev/officina/apis/officina-it-api/llms.txt) · [OpenAPI document](https://skmtc.dev/officina/apis/officina-it-api/revisions/7da3fccfe551?raw)
