{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/joinways-app/json-schema/joinways-app-quote-schema.json",
  "title": "Quote",
  "description": "The full quote header (`GET /quotes/{id}` as `quote`, `POST /quotes`, `PATCH /quotes/{id}`, the `data` of the `quote.*` webhooks).",
  "x-generated": "2026-11-13",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/joinways-app-openapi.yml#/components/schemas/Quote",
  "allOf": [
    {
      "$ref": "#/$defs/QuoteSummary"
    },
    {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "format": "uuid"
        },
        "totals_updated_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "discount_percent": {
          "type": [
            "number",
            "null"
          ]
        },
        "discount_amount": {
          "type": [
            "number",
            "null"
          ]
        },
        "invoiced_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "invoiced_by": {
          "type": [
            "string",
            "null"
          ],
          "format": "uuid"
        },
        "invoice_pdf_path": {
          "type": [
            "string",
            "null"
          ]
        },
        "invoice_snapshot": {
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": true
        },
        "invoice_bill_to": {
          "type": [
            "object",
            "null"
          ],
          "description": "Who the invoices of this quote are addressed to, as last issued, when it is not the signatory (parents paying for a wedding, an agency): `party` (`kind` `client`, `company` with `companyId`, or `platform` with `platformName`) and `identity` (`isIndividual`, `companyName`, `firstName`, `lastName`, `vatNumber`, `registrationNumber`, `address`, `postalCode`, `city`, `state`, `country`, `phone`, `purchaseOrder`). `null`: the `client_*` recipient. Read only; an accounting sync should prefer it to `client_*`.",
          "properties": {
            "party": {
              "type": "object",
              "additionalProperties": true
            },
            "identity": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "payment_terms": {
          "type": [
            "string",
            "null"
          ]
        },
        "cancellation_terms": {
          "type": [
            "string",
            "null"
          ]
        },
        "postponement_terms": {
          "type": [
            "string",
            "null"
          ]
        },
        "purchase_order": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_first_name": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_last_name": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_email": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_phone": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_is_individual": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "client_company_name": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_address": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_postal_code": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_city": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_state": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_country": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_vat_number": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_registration_number": {
          "type": [
            "string",
            "null"
          ]
        },
        "client_purchase_order": {
          "type": [
            "string",
            "null"
          ]
        },
        "parent_quote_id": {
          "type": [
            "string",
            "null"
          ],
          "format": "uuid",
          "description": "The quote this one amends (amendment)."
        },
        "amendment_sequence": {
          "type": "integer"
        },
        "platform_request_token": {
          "type": [
            "string",
            "null"
          ]
        },
        "translations": {
          "type": "object",
          "additionalProperties": true
        },
        "space_plan_snapshot": {
          "type": [
            "object",
            "array",
            "null"
          ]
        },
        "space_plan_snapshot_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "ai_generated": {
          "type": "boolean"
        },
        "ai_source_lead_id": {
          "type": [
            "string",
            "null"
          ],
          "format": "uuid"
        },
        "ai_model": {
          "type": [
            "string",
            "null"
          ]
        },
        "ai_confidence": {
          "type": [
            "number",
            "null"
          ]
        },
        "ai_reasoning": {
          "type": [
            "string",
            "null"
          ]
        },
        "ai_warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "ai_generated_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "document_lines": {
          "type": [
            "array",
            "null"
          ],
          "description": "Platform quotes only: the lines read out of the PDF the platform sent, read-only. They never count in the totals (the quote keeps one line carrying the printed amount). Null on a Joinways quote; empty until read (see `document_lines_status`).",
          "items": {
            "$ref": "#/$defs/QuoteDocumentLine"
          }
        },
        "document_lines_status": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "pending",
            "done",
            "failed",
            "no_pdf",
            "skipped",
            null
          ],
          "description": "Where the reading of the platform PDF stands. Null = not asked (only won platform quotes are read automatically)."
        },
        "document_lines_extracted_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "rejected_with_event": {
          "type": "boolean"
        },
        "is_demo": {
          "type": "boolean"
        }
      }
    }
  ],
  "$defs": {
    "QuoteDocumentLine": {
      "type": "object",
      "description": "One line printed on a platform quote PDF, as read. Every field but `id` and `title` may be null.",
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid"
        },
        "title": {
          "type": "string"
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "section": {
          "type": [
            "string",
            "null"
          ],
          "description": "Heading printed above the line on the document."
        },
        "quantity": {
          "type": [
            "number",
            "null"
          ]
        },
        "unit": {
          "type": [
            "string",
            "null"
          ]
        },
        "day": {
          "type": [
            "string",
            "null"
          ],
          "format": "date",
          "description": "Event day the line belongs to, when the document says."
        },
        "amount_excl_tax": {
          "type": [
            "number",
            "null"
          ]
        },
        "amount_incl_tax": {
          "type": [
            "number",
            "null"
          ]
        },
        "is_option": {
          "type": "boolean"
        },
        "is_discount": {
          "type": "boolean"
        }
      }
    },
    "QuoteStatus": {
      "type": "string",
      "enum": [
        "draft",
        "pending",
        "accepted",
        "signed",
        "rejected",
        "invoiced",
        "paid",
        "cancelled"
      ],
      "description": "`draft` = not sent yet; `pending` = sent, awaiting the client; `accepted` / `signed` = client agreement (signed = signature recorded); `rejected` = refused; `invoiced` = at least one invoice issued (a deposit included); `paid` = fully settled; `cancelled` = withdrawn. accepted, signed, invoiced and paid are live contracts counted in the event's confirmed amount (unless replaced, see `superseded_by_quote_id`)."
    },
    "QuoteSummary": {
      "type": "object",
      "description": "A quote as listed (`GET /quotes`).",
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid"
        },
        "event_id": {
          "type": "string",
          "format": "uuid"
        },
        "quote_number": {
          "type": [
            "string",
            "null"
          ]
        },
        "title": {
          "type": "string"
        },
        "status": {
          "$ref": "#/$defs/QuoteStatus"
        },
        "client_visible": {
          "type": "boolean",
          "description": "A draft already shown on the client page without being sent (« Montrer au client sans envoyer »): still `draft`, but the client can read and sign it. See `POST /quotes/{id}/client-visibility`."
        },
        "currency": {
          "type": "string"
        },
        "total_excl_tax": {
          "type": "number"
        },
        "total_tax": {
          "type": "number"
        },
        "total_incl_tax": {
          "type": "number"
        },
        "is_signed": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "signed_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "signature_mode": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "manual",
            "public",
            null
          ]
        },
        "signer_name": {
          "type": [
            "string",
            "null"
          ]
        },
        "signer_email": {
          "type": [
            "string",
            "null"
          ]
        },
        "is_invoiced": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "invoice_number": {
          "type": [
            "string",
            "null"
          ]
        },
        "invoice_date": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "due_date": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "payment_date": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "is_reverse_charge": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "sent_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "When the quote was sent (for a platform quote, when the platform transmitted it)."
        },
        "viewed_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "last_viewed_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "view_count": {
          "type": "integer"
        },
        "expires_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Validity end of the offer."
        },
        "created_at": {
          "type": "string",
          "format": "date-time"
        },
        "author_id": {
          "type": "string",
          "format": "uuid"
        },
        "billing_profile_id": {
          "type": [
            "string",
            "null"
          ],
          "format": "uuid"
        },
        "source": {
          "type": "string",
          "enum": [
            "joinways",
            "platform"
          ],
          "description": "`joinways` = written in Joinways. `platform` = a booking platform's own document recorded in Joinways, mostly read from the platform's emails (quote transmitted, with its PDF), or imported once signed. A platform quote is read-only: its amounts, discounts, currency and lines are never edited, it is never deleted nor duplicated, and its status only takes `accepted` or `rejected` through the API; its PDF is the platform's."
        },
        "source_platform": {
          "type": [
            "string",
            "null"
          ],
          "description": "Booking platform the document comes from (platform quote), or the one a Joinways quote was deposited to (e.g. Kactus)."
        },
        "external_reference": {
          "type": [
            "string",
            "null"
          ],
          "description": "The platform's own quote number (a platform that renumbers each revision gives each version its own). Unique per event."
        },
        "platform_pushed_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "When a Joinways quote was deposited on the platform (first deposit only)."
        },
        "superseded_by_quote_id": {
          "type": [
            "string",
            "null"
          ],
          "format": "uuid",
          "description": "Set when this quote was replaced: by a later version the booking platform sent, or by a signed amendment. A replaced quote keeps its status but never counts in the event's confirmed amount; only quotes where this is null are live. Set by Joinways only."
        },
        "superseded_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "When the quote was replaced (null while it is live)."
        }
      }
    }
  }
}