{
  "openapi": "3.1.0",
  "info": {
    "title": "Videographer.com API",
    "version": "1.0.0",
    "description": "Turn a described video shoot into a calibrated, structured, priced request — and create a real, routable request object. Deterministic (no LLM), calibrated to real production rates. No auth, no key, free.",
    "contact": {
      "name": "Videographer.com",
      "url": "https://videographer.com/agents"
    }
  },
  "servers": [
    {
      "url": "https://videographer.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Pricing",
      "description": "Describe a shoot → a structured request with a calibrated budget estimate."
    },
    {
      "name": "Orders",
      "description": "Create a real, bookmarkable order and route it to matching videographers."
    }
  ],
  "paths": {
    "/api/quote": {
      "post": {
        "tags": [
          "Pricing"
        ],
        "summary": "Price a described shoot",
        "operationId": "quote",
        "description": "Natural-language shoot description in, a structured request + calibrated budget estimate out. This is the engine behind the homepage builder.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "example": {
                "description": "a 5-hour wedding in Austin, two cameras, drone"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A structured request and a budget estimate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing description.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/estimate": {
      "post": {
        "tags": [
          "Pricing"
        ],
        "summary": "Price an already-structured request",
        "operationId": "estimate",
        "description": "If you already have a structured request (shoot type, city tier, duration, cameras, crew, add-ons), get just the budget estimate. Deterministic.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateRequest"
              },
              "example": {
                "shootType": "wedding",
                "cityTier": 1,
                "durationMinutes": 300,
                "cameras": 2,
                "crew": 1,
                "addons": [
                  "drone"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The budget estimate.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "estimate": {
                      "$ref": "#/components/schemas/Estimate"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/order": {
      "post": {
        "tags": [
          "Orders"
        ],
        "summary": "Create a request",
        "operationId": "createOrder",
        "description": "Create a real, bookmarkable order object from a described shoot. Returns an id and an owner token — keep the token to broadcast + manage the order.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "example": {
                "description": "brand film, half day, one location, Chicago"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created order id + owner token.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "token": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Owner capability — required to broadcast/manage. Keep it secret."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing description or unrecognized shoot type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/order/{id}": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "Get order status",
        "operationId": "getOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "The live status of a request derived from its routes — open, routed, or accepted (with the accepting videographer once accepted).",
        "responses": {
          "200": {
            "description": "The order status view.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderStatus"
                }
              }
            }
          },
          "404": {
            "description": "No such order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/order/{id}/broadcast": {
      "post": {
        "tags": [
          "Orders"
        ],
        "summary": "Broadcast a request to matching videographers",
        "operationId": "broadcastOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "Route the order to matching local videographers (pull-model inbox). Requires the owner token. Idempotent — re-broadcasting a routed order is a no-op.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The owner token from create."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Routed count.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "routed": {
                      "type": "integer"
                    },
                    "alreadyBroadcast": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Wrong or missing owner token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "QuoteRequest": {
        "type": "object",
        "required": [
          "description"
        ],
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Plain-language shoot description."
          }
        }
      },
      "EstimateRequest": {
        "type": "object",
        "properties": {
          "shootType": {
            "type": "string",
            "enum": [
              "wedding",
              "corporate-event",
              "brand-film",
              "real-estate",
              "music-video",
              "other"
            ]
          },
          "cityTier": {
            "type": "integer",
            "enum": [
              1,
              2,
              3
            ],
            "description": "Cost band: 1 = highest-cost metros (SF/LA), 2 = major metros (Chicago/Boston/Seattle), 3 = standard markets. A finer per-city cityFactor is applied on top."
          },
          "durationMinutes": {
            "type": "integer"
          },
          "cameras": {
            "type": "integer"
          },
          "crew": {
            "type": "integer"
          },
          "addons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "e.g. drone, second-shooter, same-day-edit."
          }
        }
      },
      "Order": {
        "type": "object",
        "description": "A structured shoot order, parsed from the description.",
        "properties": {
          "shootType": {
            "type": "string",
            "enum": [
              "wedding",
              "corporate-event",
              "brand-film",
              "real-estate",
              "music-video",
              "other"
            ]
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "cityTier": {
            "type": "integer"
          },
          "cityFactor": {
            "type": "number",
            "description": "City cost multiplier applied to the estimate."
          },
          "date": {
            "type": "string",
            "nullable": true
          },
          "durationMinutes": {
            "type": "integer"
          },
          "cameras": {
            "type": "integer"
          },
          "addons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "budget": {
            "type": "string",
            "nullable": true
          },
          "budgetAmount": {
            "type": "integer",
            "nullable": true,
            "description": "Client-stated budget, if any."
          }
        }
      },
      "Tier": {
        "type": "object",
        "description": "One of three package tiers spanning the range.",
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "essentials",
              "signature",
              "cinematic"
            ]
          },
          "label": {
            "type": "string"
          },
          "tagline": {
            "type": "string"
          },
          "priceLow": {
            "type": "integer"
          },
          "priceHigh": {
            "type": "integer"
          },
          "display": {
            "type": "string"
          },
          "includes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Estimate": {
        "type": "object",
        "description": "A calibrated budget estimate — a planning range, not a quote. Includes three package tiers and the line items + assumptions behind the number.",
        "properties": {
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "range": {
            "type": "object",
            "properties": {
              "low": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              }
            }
          },
          "display": {
            "type": "string",
            "example": "$1,675 – $3,650"
          },
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tier"
            }
          },
          "recommended": {
            "type": "object",
            "properties": {
              "cameras": {
                "type": "integer"
              },
              "crew": {
                "type": "integer"
              },
              "coverageHours": {
                "type": "integer"
              },
              "deliverables": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "turnaround": {
                "type": "string"
              }
            }
          },
          "lineItems": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "low": {
                  "type": "integer"
                },
                "high": {
                  "type": "integer"
                }
              }
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "properties": {
          "order": {
            "$ref": "#/components/schemas/Order"
          },
          "estimate": {
            "$ref": "#/components/schemas/Estimate"
          }
        }
      },
      "OrderStatus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "routed",
              "accepted"
            ]
          },
          "matchCount": {
            "type": "integer",
            "description": "Videographers matching this order."
          },
          "routedCount": {
            "type": "integer",
            "description": "How many it has been broadcast to."
          },
          "shootType": {
            "type": "string"
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "order": {
            "$ref": "#/components/schemas/Order"
          },
          "estimate": {
            "$ref": "#/components/schemas/Estimate"
          },
          "description": {
            "type": "string"
          },
          "videographer": {
            "type": "object",
            "nullable": true,
            "description": "The accepting videographer once accepted (name + optional site).",
            "properties": {
              "name": {
                "type": "string"
              },
              "url": {
                "type": "string"
              }
            }
          },
          "isOwner": {
            "type": "boolean",
            "description": "True when the request carried the owner token."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}