{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/frihet-io/json-schema/frihet-io-invoice-create-schema.json",
  "title": "InvoiceCreate",
  "x-generated": "2026-11-09",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/frihet-io-openapi.json#/components/schemas/InvoiceCreate",
  "allOf": [
    {
      "$ref": "#/$defs/InvoiceFields"
    },
    {
      "type": "object",
      "properties": {
        "provisionalDraft": {
          "type": "boolean",
          "default": false,
          "writeOnly": true,
          "description": "Create-only opt-in. true with status draft and no caller-supplied\ndocumentNumber creates an unnumbered draft without consuming a\nsequence. Omitted or false preserves immediate final numbering.\nIgnored for other create cases and on PATCH/PUT; never persisted.\nIssuing the draft with PATCH/PUT status sent assigns the final\nnumber synchronously in the same transaction as the field edits.\n"
        },
        "documentNature": {
          "type": "string",
          "enum": [
            "complete"
          ],
          "description": "Explicit declaration for a newly issued complete invoice. Requires\nstatus sent, automatic numbering (omit documentNumber), complete\nstructured issuer and recipient addresses, tax IDs and described\nlines. Draft/import requests with this declaration are rejected.\nNature receipts are server-owned and cannot be supplied or backfilled\nfor historical invoices. Simplified invoice issuance is not enabled\nby this field. Omission preserves the existing ordinary create contract.\n"
        },
        "items": {
          "type": "array",
          "items": {
            "allOf": [
              {
                "$ref": "#/$defs/LineItem"
              },
              {
                "type": "object",
                "properties": {
                  "quantity": {
                    "type": "number",
                    "minimum": 0.0001,
                    "maximum": 1000000
                  },
                  "unitPrice": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 10000000,
                    "description": "Ordinary invoices require non-negative prices. Use the credit-note operation for rectifications."
                  }
                }
              }
            ]
          },
          "minItems": 1
        }
      }
    }
  ],
  "$defs": {
    "InvoiceFields": {
      "type": "object",
      "required": [
        "clientName",
        "items"
      ],
      "properties": {
        "clientId": {
          "type": "string",
          "description": "Client document ID. When provided, the API auto-snapshots\n`clientTaxId`, `clientAddress`, `clientLocation`, `clientName`,\nand `clientEmail` from the referenced client at create time\n(immutable post-create — VeriFactu RD 1007/2023). Caller-provided\nvalues for those fields override the snapshot.\n"
        },
        "clientName": {
          "type": "string",
          "description": "Client name (snapshot field — frozen at create)"
        },
        "clientTaxId": {
          "type": "string",
          "description": "Client tax ID / NIF / VAT (snapshot field — frozen at create).\nAuto-populated from clientId reference if not provided.\n"
        },
        "clientAddress": {
          "type": "string",
          "description": "Client billing address — single-line or structured. Snapshot\nfield, frozen at create.\n"
        },
        "clientLocation": {
          "type": "string",
          "enum": [
            "peninsula",
            "canarias",
            "ceuta_melilla",
            "eu",
            "world"
          ],
          "description": "Customer fiscal zone. This does not determine an exemption or place\nof supply. Snapshot field; auto-populated from clientId if absent.\n"
        },
        "operationType": {
          "type": "string",
          "enum": [
            "service",
            "goods"
          ],
          "description": "Nature of the operation; does not itself establish its tax treatment."
        },
        "fiscalTreatment": {
          "type": "string",
          "enum": [
            "not_subject_location",
            "export_exempt"
          ],
          "description": "Explicitly reviewed legal treatment, optional on drafts. Required before\nissuing a zero-rate international invoice. The supported scope is an\nissuer established in mainland Spain/Balearics (IVA) and a non-EU customer:\nservice not subject under place-of-supply rules (not_subject_location),\nor goods export exempt under article 21 LIVA (export_exempt).\nDo not infer it from the country or tax ID. Unsupported/contradictory\nfacts return INVOICE_FISCAL_CLASSIFICATION_REQUIRED before issuance.\nImmutable after the fiscal hash exists; legacy rejected records require\nan explicit correction workflow, not editing and resending the old record.\n"
        },
        "clientEmail": {
          "type": "string",
          "format": "email",
          "description": "Client email (snapshot field — frozen at create)."
        },
        "documentNumber": {
          "type": "string",
          "maxLength": 50,
          "description": "Optional caller-supplied invoice number (e.g. migration or\nimport flows carrying an externally-issued number). When\nprovided it is honored verbatim and the workspace auto-numbering\ncounter is fast-forwarded when the supplied number belongs to its\nactive number space. On create, omission normally generates a final\nsequential number immediately. The explicit provisionalDraft: true\nopt-in with status draft and no supplied number instead creates an\nunnumbered draft; PATCH or PUT status sent assigns its final number\nsynchronously. The flag is never persisted.\n"
        },
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/LineItem"
          },
          "minItems": 1
        },
        "issueDate": {
          "type": "string",
          "format": "date",
          "description": "Issue date in ISO format"
        },
        "dueDate": {
          "type": "string",
          "format": "date",
          "description": "Due date in ISO format"
        },
        "status": {
          "$ref": "#/$defs/InvoiceStatus"
        },
        "notes": {
          "type": "string",
          "description": "Additional notes"
        },
        "taxRate": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "description": "Tax rate percentage. Combined with `clientLocation` to produce\nthe label (IGIC for canarias, IPSI for ceuta_melilla, etc.).\n"
        },
        "irpfRate": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "description": "IRPF withholding rate percentage (Spain)."
        },
        "discountRate": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "description": "Invoice-level discount percentage."
        },
        "recurring": {
          "$ref": "#/$defs/InvoiceRecurringConfig"
        }
      }
    },
    "InvoiceRecurringConfig": {
      "type": "object",
      "required": [
        "enabled",
        "frequency",
        "nextDate"
      ],
      "description": "Recurring invoice configuration. Set on a template invoice to auto-generate copies on schedule.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Whether recurring generation is active"
        },
        "frequency": {
          "type": "string",
          "enum": [
            "weekly",
            "biweekly",
            "monthly",
            "quarterly",
            "yearly"
          ],
          "description": "How often to generate a new invoice"
        },
        "nextDate": {
          "type": "string",
          "format": "date",
          "description": "Next scheduled generation date (ISO YYYY-MM-DD)"
        },
        "endDate": {
          "type": "string",
          "format": "date",
          "description": "Stop generating after this date (optional)"
        },
        "maxOccurrences": {
          "type": "integer",
          "minimum": 1,
          "description": "Maximum number of invoices to generate (optional)"
        },
        "autoSend": {
          "type": "boolean",
          "description": "Automatically send generated invoices to the client",
          "default": false
        },
        "occurrencesGenerated": {
          "type": "integer",
          "readOnly": true,
          "description": "Number of invoices generated so far (server-managed, read-only)"
        },
        "lastGeneratedId": {
          "type": "string",
          "readOnly": true,
          "description": "ID of the last auto-generated invoice (server-managed, read-only)"
        }
      }
    },
    "InvoiceStatus": {
      "type": "string",
      "enum": [
        "draft",
        "sent",
        "partial",
        "paid",
        "overdue",
        "cancelled"
      ]
    },
    "LineItem": {
      "type": "object",
      "required": [
        "description",
        "quantity",
        "unitPrice"
      ],
      "properties": {
        "description": {
          "type": "string",
          "description": "Item description"
        },
        "quantity": {
          "type": "number",
          "description": "Quantity"
        },
        "unitPrice": {
          "type": "number",
          "description": "Price per unit"
        }
      }
    }
  }
}