{
  "openapi": "3.1.0",
  "info": {
    "title": "Bookly Support API",
    "version": "1.0.0",
    "summary": "Order, shipping, returns, refunds and support knowledge API for the Bookly demo bookstore.",
    "description": "Public, unauthenticated demo API for building a Bookly customer-support agent.\n\n**Conventions**\n- Success responses are `{ \"data\": ..., \"meta\": { \"request_id\": ... } }`.\n- Errors are RFC 9457-style: `{ \"error\": { \"type\", \"title\", \"status\", \"code\", \"detail\", \"request_id\" } }`.\n- Money is always integer cents.\n- Orders accept either a UUID or a human order number (`BK-10042`). Customers accept UUID or email. Returns accept `RMA-#####`.\n- Lists are paginated with `limit` (max 100) and `offset`; `meta` carries `total`, `limit`, `offset`, `has_more`.\n- CORS is open and no API key is required.\n\n**Business rules**\n- Return window: 30 days from delivery (90 days for damaged or wrong-item claims).\n- Return label fee: $4.99, waived for damaged/wrong-item.\n- Free shipping over $35.00, otherwise $4.99 ground.\n- Orders can be cancelled while processing or backordered; ebooks are never returnable.\n\nStart with `GET /api/public/v1/meta` to discover live identifiers.",
    "contact": {
      "name": "Bookly API",
      "url": "https://bookly.davidbusacker.com/docs"
    },
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://bookly.davidbusacker.com",
      "description": "Bookly demo server"
    }
  ],
  "tags": [
    {
      "name": "System",
      "description": "Health and discovery."
    },
    {
      "name": "Orders",
      "description": "Order lookup, cancellation and line items."
    },
    {
      "name": "Shipping",
      "description": "Shipments, tracking events and reshipments."
    },
    {
      "name": "Returns",
      "description": "Eligibility, RMA creation and lifecycle."
    },
    {
      "name": "Refunds",
      "description": "Refunds, store credit and the money ledger."
    },
    {
      "name": "Customers",
      "description": "Customer identity and order history."
    },
    {
      "name": "Catalog",
      "description": "Book catalog search."
    },
    {
      "name": "Knowledge",
      "description": "Policies and FAQs for grounded answers."
    },
    {
      "name": "Support",
      "description": "Tickets and simulated support actions."
    }
  ],
  "paths": {
    "/api/public/v1/health": {
      "get": {
        "operationId": "health",
        "tags": [
          "System"
        ],
        "summary": "Service health",
        "description": "Liveness probe plus database connectivity check and API version.\n\n**Agent guidance:** Call once at startup to confirm the API and dataset are reachable.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/meta": {
      "get": {
        "operationId": "meta",
        "tags": [
          "System"
        ],
        "summary": "Discovery metadata",
        "description": "Row counts, live sample identifiers (order numbers, emails, RMAs, tracking numbers), every enum value and the business-rule constants. This is the recommended bootstrap call for an agent.\n\n**Agent guidance:** Fetch first so you can use real identifiers in later calls instead of guessing.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/orders": {
      "get": {
        "operationId": "listOrders",
        "tags": [
          "Orders"
        ],
        "summary": "List / search orders",
        "description": "Paginated order list with filters. Use `email` when a customer contacts support without an order number.\n\n**Agent guidance:** Primary lookup when the customer gives an email address rather than an order number.",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Exact customer email.",
            "schema": {
              "type": "string"
            },
            "example": "ava.brooks@example.com"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated list of order statuses.",
            "schema": {
              "type": "string",
              "enum": [
                "processing",
                "shipped",
                "delivered",
                "cancelled",
                "returned",
                "refunded",
                "backordered",
                "lost_in_transit"
              ]
            },
            "example": "shipped,delivered"
          },
          {
            "name": "order_number",
            "in": "query",
            "required": false,
            "description": "Exact order number, e.g. BK-10042.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Fuzzy search across order number and customer email.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "placed_after",
            "in": "query",
            "required": false,
            "description": "ISO date lower bound on placed_at.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "placed_before",
            "in": "query",
            "required": false,
            "description": "ISO date upper bound on placed_at.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Get one order",
        "description": "Full order with customer, line items, shipments, returns, refunds and transactions embedded.\n\n**Agent guidance:** The single richest call for answering 'where is my order?'.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order UUID or human order number (e.g. BK-10042). Both are accepted everywhere.",
            "schema": {
              "type": "string"
            },
            "example": "BK-10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/orders/{id}/items": {
      "get": {
        "operationId": "getOrderItems",
        "tags": [
          "Orders"
        ],
        "summary": "List order line items",
        "description": "Line items with the joined book record (format matters for return eligibility).\n\n**Agent guidance:** Use the returned order_item_id values when creating a partial return.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order UUID or human order number (e.g. BK-10042). Both are accepted everywhere.",
            "schema": {
              "type": "string"
            },
            "example": "BK-10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/orders/{id}/shipments": {
      "get": {
        "operationId": "getOrderShipments",
        "tags": [
          "Shipping"
        ],
        "summary": "List shipments for an order",
        "description": "All shipments for the order, each with its full tracking event history.\n\n**Agent guidance:** Answer delivery-timing questions and detect stalled tracking.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order UUID or human order number (e.g. BK-10042). Both are accepted everywhere.",
            "schema": {
              "type": "string"
            },
            "example": "BK-10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/orders/{id}/cancel": {
      "post": {
        "operationId": "cancelOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Cancel an order",
        "description": "Cancels an order and voids the authorization. Only allowed while the status is processing or backordered.\n\n**Agent guidance:** Use for 'cancel my order'. If it returns 409, offer a return instead.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order UUID or human order number (e.g. BK-10042). Both are accepted everywhere.",
            "schema": {
              "type": "string"
            },
            "example": "BK-10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Free-text cancellation reason."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "reason": "Ordered by mistake"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/orders/{id}/reship": {
      "post": {
        "operationId": "reshipOrder",
        "tags": [
          "Shipping"
        ],
        "summary": "Create a replacement shipment",
        "description": "Generates a new shipment and tracking number. Allowed when the order is lost_in_transit or shipped.\n\n**Agent guidance:** Use for lost-in-transit packages after confirming tracking is stalled.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order UUID or human order number (e.g. BK-10042). Both are accepted everywhere.",
            "schema": {
              "type": "string"
            },
            "example": "BK-10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "carrier": {
                    "type": "string",
                    "description": "Carrier, default UPS.",
                    "enum": [
                      "UPS",
                      "FedEx",
                      "USPS",
                      "DHL"
                    ]
                  },
                  "service_level": {
                    "type": "string",
                    "description": "Service level, default two_day.",
                    "enum": [
                      "ground",
                      "two_day",
                      "overnight"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "description": "Why the reshipment was issued."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "reason": "Package lost in transit"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/orders/{id}/returns/eligibility": {
      "post": {
        "operationId": "returnEligibility",
        "tags": [
          "Returns"
        ],
        "summary": "Check return eligibility",
        "description": "Deterministic eligibility check. Standard window is 30 days from delivery; damaged or wrong-item claims get 90 days and waive the $4.99 label fee. Ebooks are non-returnable.\n\n**Agent guidance:** Always call this before creating a return so you can explain the outcome to the customer.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order UUID or human order number (e.g. BK-10042). Both are accepted everywhere.",
            "schema": {
              "type": "string"
            },
            "example": "BK-10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Intended return reason.",
                    "enum": [
                      "damaged",
                      "wrong_item",
                      "not_as_described",
                      "no_longer_needed",
                      "arrived_late",
                      "other"
                    ]
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "reason": "damaged"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/returns": {
      "get": {
        "operationId": "listReturns",
        "tags": [
          "Returns"
        ],
        "summary": "List returns",
        "description": "Paginated returns with their items and parent order.\n\n**Agent guidance:** Check whether a return already exists before creating a duplicate.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated return statuses.",
            "schema": {
              "type": "string",
              "enum": [
                "requested",
                "label_sent",
                "in_transit",
                "received",
                "refunded",
                "rejected",
                "cancelled"
              ]
            }
          },
          {
            "name": "order_id",
            "in": "query",
            "required": false,
            "description": "Filter by order UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "description": "Filter by customer UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createReturn",
        "tags": [
          "Returns"
        ],
        "summary": "Create a return (RMA)",
        "description": "Creates an RMA with a prepaid label and computes the expected refund. Rejects with 409 when the order is not eligible unless `override_eligibility` is true.\n\n**Agent guidance:** Call after eligibility passes. `items` is optional — omit it to return every line on the order, or match lines by order_item_id, isbn or title. Set initiate_refund to open the refund in the same call.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "order_id": {
                    "type": "string",
                    "description": "Order UUID or order number."
                  },
                  "reason": {
                    "type": "string",
                    "description": "Return reason.",
                    "enum": [
                      "damaged",
                      "wrong_item",
                      "not_as_described",
                      "no_longer_needed",
                      "arrived_late",
                      "other"
                    ]
                  },
                  "comment": {
                    "type": "string",
                    "description": "Customer's description of the problem."
                  },
                  "items": {
                    "type": "array",
                    "description": "Line items to return. Omit to return every item on the order.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "order_item_id": {
                          "type": "string",
                          "description": "Order item UUID."
                        },
                        "isbn": {
                          "type": "string",
                          "description": "Match the line by ISBN instead of UUID."
                        },
                        "title": {
                          "type": "string",
                          "description": "Match the line by (partial) book title."
                        },
                        "quantity": {
                          "type": "integer",
                          "description": "Units returned; defaults to the full line quantity."
                        }
                      },
                      "additionalProperties": false
                    }
                  },
                  "initiate_refund": {
                    "type": "string",
                    "description": "Open a refund with the RMA: `pending_return` parks the money until the book is received, `immediate` settles it now (loyalty / goodwill).",
                    "enum": [
                      "none",
                      "pending_return",
                      "immediate"
                    ]
                  },
                  "actor": {
                    "type": "string",
                    "description": "Who is acting, recorded on the refund timeline."
                  },
                  "override_eligibility": {
                    "type": "boolean",
                    "description": "Supervisor override that bypasses the eligibility gate."
                  }
                },
                "required": [
                  "order_id",
                  "reason"
                ],
                "additionalProperties": false
              },
              "example": {
                "order_id": "BK-10042",
                "reason": "damaged",
                "items": [
                  {
                    "order_item_id": "<uuid>",
                    "quantity": 1
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/public/v1/returns/{rma}": {
      "get": {
        "operationId": "getReturn",
        "tags": [
          "Returns"
        ],
        "summary": "Get a return",
        "description": "Fetch a return by RMA number (RMA-12345) or UUID.\n\n**Agent guidance:** Answer 'what is the status of my return?'.",
        "parameters": [
          {
            "name": "rma",
            "in": "path",
            "required": true,
            "description": "RMA number or UUID.",
            "schema": {
              "type": "string"
            },
            "example": "RMA-12345"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/returns/{rma}/receive": {
      "post": {
        "operationId": "receiveReturn",
        "tags": [
          "Returns"
        ],
        "summary": "Mark a return received",
        "description": "Moves the return to `received`, optionally issuing the refund immediately.\n\n**Agent guidance:** Warehouse-side action; set auto_refund to close the loop in one call.",
        "parameters": [
          {
            "name": "rma",
            "in": "path",
            "required": true,
            "description": "RMA number or UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "condition": {
                    "type": "string",
                    "description": "Received condition.",
                    "enum": [
                      "unopened",
                      "opened",
                      "damaged"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "description": "Inspection note."
                  },
                  "auto_refund": {
                    "type": "boolean",
                    "description": "Issue the refund immediately."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "auto_refund": true
              }
            }
          }
        }
      }
    },
    "/api/public/v1/returns/{rma}/cancel": {
      "post": {
        "operationId": "cancelReturn",
        "tags": [
          "Returns"
        ],
        "summary": "Cancel a return",
        "description": "Cancels an open RMA. Fails with 409 once the return is refunded.\n\n**Agent guidance:** Use when the customer decides to keep the item.",
        "parameters": [
          {
            "name": "rma",
            "in": "path",
            "required": true,
            "description": "RMA number or UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/refunds": {
      "get": {
        "operationId": "listRefunds",
        "tags": [
          "Refunds"
        ],
        "summary": "List refunds",
        "description": "Paginated refunds with their parent order.\n\n**Agent guidance:** Confirm whether money already went back before promising a new refund.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated refund statuses.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "pending_return",
                "processing",
                "succeeded",
                "failed",
                "cancelled"
              ]
            }
          },
          {
            "name": "order_id",
            "in": "query",
            "required": false,
            "description": "Filter by order UUID or order number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createRefund",
        "tags": [
          "Refunds"
        ],
        "summary": "Issue a refund",
        "description": "Issues a full or partial refund, records the matching ledger transaction, credits store credit when requested and flips the order to `refunded` once fully covered. Over-refunding is rejected.\n\n**Agent guidance:** Use for goodwill refunds and post-return settlements.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "order_id": {
                    "type": "string",
                    "description": "Order UUID or order number."
                  },
                  "amount_cents": {
                    "type": "integer",
                    "description": "Defaults to the full remaining refundable amount."
                  },
                  "method": {
                    "type": "string",
                    "description": "Default original_payment.",
                    "enum": [
                      "original_payment",
                      "store_credit"
                    ]
                  },
                  "return_id": {
                    "type": "string",
                    "description": "Link the refund to an existing return."
                  },
                  "status": {
                    "type": "string",
                    "description": "Initial status. Default `succeeded` (money moves now). Use `pending_return` to promise a refund once the book is received.",
                    "enum": [
                      "pending",
                      "pending_return",
                      "processing",
                      "succeeded",
                      "failed",
                      "cancelled"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "description": "Reason recorded on the ledger."
                  },
                  "note": {
                    "type": "string",
                    "description": "Free-text note stored on the refund timeline."
                  },
                  "actor": {
                    "type": "string",
                    "description": "Who is acting (e.g. support_agent, ai_agent)."
                  }
                },
                "required": [
                  "order_id"
                ],
                "additionalProperties": false
              },
              "example": {
                "order_id": "BK-10042",
                "amount_cents": 500,
                "reason": "Late delivery goodwill"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/refunds/{id}": {
      "get": {
        "operationId": "getRefund",
        "tags": [
          "Refunds"
        ],
        "summary": "Get a refund",
        "description": "Fetch a refund by refund number (RF-12345) or UUID.\n\n**Agent guidance:** Give the customer the exact refund amount and timing.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Refund number or UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "operationId": "updateRefund",
        "tags": [
          "Refunds"
        ],
        "summary": "Update a refund status",
        "description": "Moves a refund to any status. Settling (`succeeded`) writes the ledger transaction, applies store credit and flips the order to refunded once fully covered; cancelling or failing a settled refund writes a reversal. Every change appends to the refund timeline (`events`).\n\n**Agent guidance:** Use to release a `pending_return` refund early for a loyal customer, or to cancel a refund the customer no longer wants.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Refund number or UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "Target status.",
                    "enum": [
                      "pending",
                      "pending_return",
                      "processing",
                      "succeeded",
                      "failed",
                      "cancelled"
                    ]
                  },
                  "amount_cents": {
                    "type": "integer",
                    "description": "Adjust the amount (only before it settles)."
                  },
                  "method": {
                    "type": "string",
                    "description": "Switch payout method.",
                    "enum": [
                      "original_payment",
                      "store_credit"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "description": "Updated reason."
                  },
                  "note": {
                    "type": "string",
                    "description": "Why the decision was made — shown on the timeline."
                  },
                  "actor": {
                    "type": "string",
                    "description": "Who is acting."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "status": "succeeded",
                "actor": "ai_agent",
                "note": "Loyal customer since 2019 — releasing refund before return arrives"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/transactions": {
      "get": {
        "operationId": "listTransactions",
        "tags": [
          "Refunds"
        ],
        "summary": "List ledger transactions",
        "description": "Immutable money ledger: charges, refunds and voids.\n\n**Agent guidance:** Reconcile what the customer was actually charged.",
        "parameters": [
          {
            "name": "order_id",
            "in": "query",
            "required": false,
            "description": "Filter by order UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "description": "Filter by customer UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Comma-separated types.",
            "schema": {
              "type": "string",
              "enum": [
                "charge",
                "refund",
                "void"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated statuses.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/customers": {
      "get": {
        "operationId": "listCustomers",
        "tags": [
          "Customers"
        ],
        "summary": "List / search customers",
        "description": "Search customers by email, fuzzy name/email query or loyalty tier.\n\n**Agent guidance:** Identity resolution before touching order data.",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Case-insensitive exact email.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Fuzzy name or email search.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tier",
            "in": "query",
            "required": false,
            "description": "Loyalty tier.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "tags": [
          "Customers"
        ],
        "summary": "Get a customer",
        "description": "Fetch by UUID or email (URL-encoded), including saved addresses and store credit.\n\n**Agent guidance:** Check store credit balance before offering it as a refund method.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Customer UUID or email address.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/customers/{id}/orders": {
      "get": {
        "operationId": "getCustomerOrders",
        "tags": [
          "Customers"
        ],
        "summary": "List a customer's orders",
        "description": "Order history for a customer, newest first. Accepts UUID or email.\n\n**Agent guidance:** Use when the customer says 'my last order' without a number.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Customer UUID or email.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/shipments/{tracking}": {
      "get": {
        "operationId": "getShipment",
        "tags": [
          "Shipping"
        ],
        "summary": "Track a shipment",
        "description": "Fetch a shipment by tracking number or UUID, with its events and parent order.\n\n**Agent guidance:** Direct tracking lookup when the customer pastes a tracking number.",
        "parameters": [
          {
            "name": "tracking",
            "in": "path",
            "required": true,
            "description": "Tracking number or shipment UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/shipments/{tracking}/events": {
      "get": {
        "operationId": "getShipmentEvents",
        "tags": [
          "Shipping"
        ],
        "summary": "Tracking event history",
        "description": "Chronological scan events for a shipment.\n\n**Agent guidance:** Detect a stalled package: no new scan for 7+ days means offer a reship.",
        "parameters": [
          {
            "name": "tracking",
            "in": "path",
            "required": true,
            "description": "Tracking number.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/books": {
      "get": {
        "operationId": "listBooks",
        "tags": [
          "Catalog"
        ],
        "summary": "Search the catalog",
        "description": "Search books by title, author or ISBN with category, format and stock filters.\n\n**Agent guidance:** Recommend replacements or confirm stock before promising a reship.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search title, author or ISBN.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Exact category.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Format.",
            "schema": {
              "type": "string",
              "enum": [
                "hardcover",
                "paperback",
                "ebook",
                "audiobook"
              ]
            }
          },
          {
            "name": "in_stock",
            "in": "query",
            "required": false,
            "description": "Only items with stock > 0.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/books/{id}": {
      "get": {
        "operationId": "getBook",
        "tags": [
          "Catalog"
        ],
        "summary": "Get a book",
        "description": "Fetch a book by UUID or ISBN-13.\n\n**Agent guidance:** Confirm format — ebooks cannot be returned.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Book UUID or ISBN.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/policies": {
      "get": {
        "operationId": "listPolicies",
        "tags": [
          "Knowledge"
        ],
        "summary": "List policies",
        "description": "Shipping, returns, refunds, privacy and account policies in full text.\n\n**Agent guidance:** Ground policy answers in this text instead of improvising.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Policy category.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Full-text-ish search across title and body.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/policies/{slug}": {
      "get": {
        "operationId": "getPolicy",
        "tags": [
          "Knowledge"
        ],
        "summary": "Get one policy",
        "description": "Fetch a single policy document by slug, e.g. `returns`.\n\n**Agent guidance:** Quote the exact clause back to the customer.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Policy slug.",
            "schema": {
              "type": "string"
            },
            "example": "returns"
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/faqs": {
      "get": {
        "operationId": "listFaqs",
        "tags": [
          "Knowledge"
        ],
        "summary": "Search FAQs",
        "description": "Curated question/answer pairs for common support topics.\n\n**Agent guidance:** First stop for general questions that need no account data.",
        "parameters": [
          {
            "name": "topic",
            "in": "query",
            "required": false,
            "description": "FAQ topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search question and answer text.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/public/v1/support/tickets": {
      "get": {
        "operationId": "listTickets",
        "tags": [
          "Support"
        ],
        "summary": "List support tickets",
        "description": "Paginated support tickets with customer and order context.\n\n**Agent guidance:** Check for an existing open ticket before creating another.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated statuses.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "pending",
                "resolved",
                "escalated",
                "closed"
              ]
            }
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Filter by customer email.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1-100, default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer"
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createTicket",
        "tags": [
          "Support"
        ],
        "summary": "Create a support ticket",
        "description": "Opens a ticket, linking the customer by email and optionally an order.\n\n**Agent guidance:** Escalation path when you cannot resolve the request with the other endpoints.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "customer_email": {
                    "type": "string",
                    "description": "Customer email."
                  },
                  "subject": {
                    "type": "string",
                    "description": "Short summary."
                  },
                  "body": {
                    "type": "string",
                    "description": "Full description / conversation transcript."
                  },
                  "category": {
                    "type": "string",
                    "description": "Ticket category.",
                    "enum": [
                      "order_status",
                      "return",
                      "refund",
                      "shipping",
                      "account",
                      "other"
                    ]
                  },
                  "priority": {
                    "type": "string",
                    "description": "Priority, default normal.",
                    "enum": [
                      "low",
                      "normal",
                      "high",
                      "urgent"
                    ]
                  },
                  "order_id": {
                    "type": "string",
                    "description": "Related order UUID."
                  }
                },
                "required": [
                  "customer_email",
                  "subject"
                ],
                "additionalProperties": false
              },
              "example": {
                "customer_email": "ava.brooks@example.com",
                "subject": "Package never arrived",
                "category": "shipping"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/support/tickets/{id}": {
      "patch": {
        "operationId": "updateTicket",
        "tags": [
          "Support"
        ],
        "summary": "Update a ticket",
        "description": "Change status, priority or record a resolution. Accepts TCK number or UUID.\n\n**Agent guidance:** Close the ticket once the customer confirms the issue is resolved.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Ticket number or UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "New status.",
                    "enum": [
                      "open",
                      "pending",
                      "resolved",
                      "closed"
                    ]
                  },
                  "priority": {
                    "type": "string",
                    "description": "New priority.",
                    "enum": [
                      "low",
                      "normal",
                      "high",
                      "urgent"
                    ]
                  },
                  "resolution": {
                    "type": "string",
                    "description": "Resolution summary."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "status": "resolved"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/support/actions/password-reset": {
      "post": {
        "operationId": "passwordReset",
        "tags": [
          "Support"
        ],
        "summary": "Send a password reset link",
        "description": "Simulates sending a reset email. Always returns success so account existence is never leaked.\n\n**Agent guidance:** Standard answer for 'I can't log in'.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "Account email."
                  }
                },
                "required": [
                  "email"
                ],
                "additionalProperties": false
              },
              "example": {
                "email": "ava.brooks@example.com"
              }
            }
          }
        }
      }
    },
    "/api/public/v1/support/actions/address-change": {
      "post": {
        "operationId": "addressChange",
        "tags": [
          "Support"
        ],
        "summary": "Change a shipping address",
        "description": "Updates the shipping address while the order is still pending or processing.\n\n**Agent guidance:** Use before the order ships; afterwards offer a return or intercept instead.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "order_id": {
                    "type": "string",
                    "description": "Order UUID or number."
                  },
                  "line1": {
                    "type": "string",
                    "description": "Street address."
                  },
                  "line2": {
                    "type": "string",
                    "description": "Apt / suite."
                  },
                  "city": {
                    "type": "string",
                    "description": "City."
                  },
                  "region": {
                    "type": "string",
                    "description": "State or region."
                  },
                  "postal_code": {
                    "type": "string",
                    "description": "Postal code."
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO-2 country code, default US."
                  }
                },
                "required": [
                  "order_id",
                  "line1",
                  "city",
                  "region",
                  "postal_code"
                ],
                "additionalProperties": false
              },
              "example": {
                "order_id": "BK-10042",
                "line1": "77 Foxglove Ln",
                "city": "Austin",
                "region": "TX",
                "postal_code": "78702"
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SuccessEnvelope": {
        "type": "object",
        "properties": {
          "data": {
            "description": "Resource or array of resources."
          },
          "meta": {
            "type": "object",
            "properties": {
              "request_id": {
                "type": "string"
              },
              "total": {
                "type": "integer"
              },
              "limit": {
                "type": "integer"
              },
              "offset": {
                "type": "integer"
              },
              "has_more": {
                "type": "boolean"
              }
            }
          }
        },
        "required": [
          "data",
          "meta"
        ]
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "format": "uri"
              },
              "title": {
                "type": "string"
              },
              "status": {
                "type": "integer"
              },
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "not_found",
                  "conflict",
                  "internal_error"
                ]
              },
              "detail": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            },
            "required": [
              "title",
              "status",
              "code"
            ]
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error envelope",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    }
  }
}