{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://travel.agenthaven.dev/contracts/schemas/quote.schema.json",
  "title": "Quote (payload of a quote+jwt)",
  "description": "The seller's quote, as the payload of a compact JWS with header {alg: ES256, typ: quote+jwt, kid: <RFC 7638 thumbprint of the seller instance key>}. The seller signs it with its instance key, the one the domain vouches for in the JWT-SVID served at /.well-known/spiffe-svid.json. quote_hash, wherever it appears (mandate, checkout, receipt, ledger), is SHA-256 lowercase hex over the raw decoded payload octets of this JWS: the bytes that were signed, no canonicalization. Everyone recomputes; nobody accepts another party's value. Since 2026-09-13 the payload also carries the UCP Checkout fields (id, merchant, line_items, status, totals, links), so that this one artifact is also the checkout_jwt of the AP2 v0.2 profile: see contracts/mandate-ap2-profile.md. They are additive and say the same thing as the flat claims beside them; currency is shared by both readings. Status: IMPLEMENTED by the seller (agent-bench 76baafc; UCP fields since bb10322).",
  "type": "object",
  "required": [
    "iss",
    "jti",
    "iat",
    "exp",
    "merchant_id",
    "currency",
    "total",
    "offer_id",
    "basis",
    "route",
    "provider",
    "correlation_id",
    "id",
    "merchant",
    "line_items",
    "status",
    "totals",
    "links"
  ],
  "properties": {
    "iss": {
      "type": "string",
      "format": "uri",
      "description": "The seller's public base URL."
    },
    "jti": {
      "type": "string",
      "pattern": "^q_[0-9a-f-]{36}$",
      "description": "The quote id, the input to create_checkout."
    },
    "iat": {
      "type": "integer"
    },
    "exp": {
      "type": "integer",
      "description": "Seconds since the epoch. Ten minutes after iat in the CI profile; the demo profile is set from the rehearsed approval time."
    },
    "merchant_id": {
      "type": "string"
    },
    "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "total": {
      "type": "integer",
      "minimum": 1,
      "description": "Fare in minor units of currency."
    },
    "offer_id": {
      "type": "string",
      "description": "The seller's own id for the itinerary at this fare; stable for the same flights on the same day at the same price."
    },
    "basis": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "string",
        "enum": [
          "cheapest",
          "fastest"
        ]
      }
    },
    "route": {
      "type": "object",
      "required": [
        "origin",
        "destination",
        "departure_date",
        "adults"
      ],
      "properties": {
        "origin": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "destination": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "departure_date": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        },
        "adults": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "itinerary": {
      "$ref": "#/$defs/itinerary"
    },
    "provider": {
      "type": "string",
      "description": "Where the fare came from. \"fixture\" is the announced fallback: Google answered nothing and the simulation fixtures answered instead."
    },
    "correlation_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
      "description": "The buyer's transaction id, echoed; or seller-minted when the buyer sent none (the ledger entry says which)."
    },
    "id": {
      "type": "string",
      "description": "UCP: the checkout session id. Equal to jti; both are present because each vocabulary names it its own way."
    },
    "merchant": {
      "type": "object",
      "required": [
        "id",
        "name"
      ],
      "description": "UCP/AP2: the merchant this checkout belongs to. Optional in the reference type, always sent here, because a Checkout Mandate's checkout.allowed_merchants is matched against it.",
      "properties": {
        "id": {
          "type": "string",
          "description": "Equal to merchant_id."
        },
        "name": {
          "type": "string",
          "description": "The same name the agent card advertises."
        },
        "website": {
          "type": "string",
          "format": "uri",
          "description": "Equal to iss."
        }
      }
    },
    "line_items": {
      "type": "array",
      "minItems": 1,
      "description": "UCP: what is being bought. One line item here: the itinerary at the quoted fare.",
      "items": {
        "$ref": "#/$defs/line_item"
      }
    },
    "status": {
      "type": "string",
      "enum": [
        "incomplete",
        "requires_escalation",
        "ready_for_complete",
        "complete_in_progress",
        "completed",
        "canceled"
      ],
      "description": "UCP checkout state. A quote is always issued ready_for_complete; the other values exist in the reference type and are never produced here."
    },
    "totals": {
      "type": "array",
      "description": "UCP: the pricing breakdown, exactly one subtotal and one total. No tax or fee line is invented: the fare is quoted as it came from the provider.",
      "minItems": 2,
      "items": {
        "$ref": "#/$defs/total"
      }
    },
    "links": {
      "type": "array",
      "description": "UCP: typed URL references (terms, policies). Empty: this POC publishes none, and an empty array is the honest answer rather than a link that means nothing.",
      "items": {
        "$ref": "#/$defs/link"
      }
    },
    "airlines": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^[A-Z0-9]{2,3}$"
      },
      "description": "Since 2026-09-26: present when the buyer named airlines in search_flights. The seller's signed word that this option was chosen among itineraries whose every segment is on one of these airlines, by the designator that opens the flight number."
    }
  },
  "$defs": {
    "itinerary": {
      "type": [
        "object",
        "null"
      ],
      "required": [
        "segments",
        "layovers",
        "stops"
      ],
      "properties": {
        "segments": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/segment"
          }
        },
        "layovers": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "airport"
            ],
            "properties": {
              "airport": {
                "type": "string"
              },
              "airport_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "duration_minutes": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "overnight": {
                "type": "boolean"
              }
            }
          }
        },
        "stops": {
          "type": "integer",
          "minimum": 0
        },
        "total_duration_minutes": {
          "type": [
            "integer",
            "null"
          ]
        }
      }
    },
    "segment": {
      "type": "object",
      "required": [
        "flight_number",
        "departure",
        "arrival"
      ],
      "properties": {
        "flight_number": {
          "type": "string",
          "pattern": "^[A-Z0-9]{2,3} ?\\d{1,4}[A-Z]?$"
        },
        "airline": {
          "type": [
            "string",
            "null"
          ]
        },
        "departure": {
          "$ref": "#/$defs/stop"
        },
        "arrival": {
          "$ref": "#/$defs/stop"
        },
        "duration_minutes": {
          "type": [
            "integer",
            "null"
          ]
        },
        "aircraft": {
          "type": [
            "string",
            "null"
          ]
        },
        "travel_class": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "stop": {
      "type": "object",
      "required": [
        "airport",
        "local_time"
      ],
      "properties": {
        "airport": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "airport_name": {
          "type": [
            "string",
            "null"
          ]
        },
        "local_time": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$",
          "description": "Local to the airport, no offset."
        }
      }
    },
    "line_item": {
      "type": "object",
      "required": [
        "id",
        "item",
        "quantity",
        "totals"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Equal to offer_id."
        },
        "item": {
          "type": "object",
          "required": [
            "id",
            "title",
            "price"
          ],
          "properties": {
            "id": {
              "type": "string"
            },
            "title": {
              "type": "string",
              "description": "Generated from the route, not free text: \"Flight <origin> to <destination>, <date>, <n> adult(s)\"."
            },
            "price": {
              "type": "integer",
              "minimum": 0,
              "description": "Unit price in minor units. Equal to total while quantity is 1. Required by the reference type."
            }
          }
        },
        "quantity": {
          "type": "integer",
          "minimum": 1
        },
        "totals": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/total"
          }
        }
      }
    },
    "total": {
      "type": "object",
      "required": [
        "type",
        "amount"
      ],
      "properties": {
        "type": {
          "type": "string",
          "description": "Cost category: subtotal, discount, items_discount, fulfillment, tax, fee, total."
        },
        "amount": {
          "type": "integer",
          "description": "Signed amount in minor units of currency."
        },
        "display_text": {
          "type": "string"
        }
      }
    },
    "link": {
      "type": "object",
      "required": [
        "type",
        "url"
      ],
      "properties": {
        "type": {
          "type": "string"
        },
        "url": {
          "type": "string",
          "format": "uri"
        },
        "title": {
          "type": "string"
        }
      }
    }
  }
}
