{
  "openapi": "3.0.3",
  "info": {
    "title": "Dumpster Controls API",
    "version": "1.0.0",
    "description": "Public REST API for Dumpster Controls. Read your company's own data and push in leads, always scoped to your company. Access is approval-gated: request a key in Settings, API. Free plan includes 10 minutes of API time per day; paid plans are unlimited. Money is in US dollars. This spec is versioned at /v1 and additive; breaking changes ship under a new version.",
    "contact": { "name": "Dumpster Controls", "url": "https://dumpstercontrols.io/developers" }
  },
  "servers": [
    { "url": "https://gcwyoiihrupbfqqlcurh.supabase.co/functions/v1/api-v1/v1", "description": "Production" }
  ],
  "security": [{ "bearerAuth": [] }],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http", "scheme": "bearer",
        "description": "Send your API key as a Bearer token: Authorization: Bearer dc_live_... Keys are approved in Settings, API and must be kept secret (server-side only)."
      }
    },
    "responses": {
      "Unauthorized": { "description": "Missing, invalid or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "60 requests/minute per key exceeded, or the free plan's daily 10-minute window ended.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "AccessDisabled": { "description": "API access for this company is disabled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } } },
          "upgrade_url": { "type": "string", "nullable": true }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "order_number": { "type": "string" },
          "status": { "type": "string", "enum": ["placed","confirmed","scheduled","out_for_delivery","delivered","waiting_pickup","picked_up","completed","canceled","waiting_delivery","waiting_landfill"] },
          "payment_status": { "type": "string", "nullable": true },
          "delivery_date": { "type": "string", "format": "date", "nullable": true },
          "pickup_date": { "type": "string", "format": "date", "nullable": true },
          "rental_days": { "type": "integer", "nullable": true },
          "address": { "type": "object", "properties": { "line1": { "type": "string", "nullable": true }, "city": { "type": "string", "nullable": true }, "state": { "type": "string", "nullable": true }, "zip": { "type": "string", "nullable": true } } },
          "size": { "type": "string", "nullable": true },
          "debris_type": { "type": "string", "nullable": true },
          "total_price": { "type": "number", "nullable": true, "description": "US dollars" },
          "base_price": { "type": "number", "nullable": true },
          "landfill_cost": { "type": "number", "nullable": true },
          "net_weight_tons": { "type": "number", "nullable": true },
          "customer_ref": { "type": "string", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" },
          "completed_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": { "type": "string" }, "first_name": { "type": "string" }, "last_name": { "type": "string" },
          "company_name": { "type": "string", "nullable": true }, "email": { "type": "string", "nullable": true },
          "phone": { "type": "string", "nullable": true }, "customer_type": { "type": "string" },
          "address_line1": { "type": "string", "nullable": true }, "city": { "type": "string", "nullable": true },
          "state": { "type": "string", "nullable": true }, "zip_code": { "type": "string", "nullable": true },
          "total_orders": { "type": "integer" }, "lifetime_value": { "type": "number" },
          "is_active": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Lead": {
        "type": "object",
        "properties": {
          "id": { "type": "string" }, "name": { "type": "string" }, "phone": { "type": "string", "nullable": true },
          "email": { "type": "string", "nullable": true }, "address": { "type": "string", "nullable": true },
          "size_label": { "type": "string", "nullable": true }, "note": { "type": "string", "nullable": true },
          "status": { "type": "string", "enum": ["new","contacted","converted","discarded"] },
          "source": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Size": {
        "type": "object",
        "properties": {
          "label": { "type": "string" }, "yards": { "type": "number" }, "base_price": { "type": "number" },
          "max_weight_tons": { "type": "number", "nullable": true }, "overage_price_per_ton": { "type": "number", "nullable": true },
          "is_active": { "type": "boolean" }
        }
      },
      "JunkJob": {
        "type": "object",
        "properties": {
          "job_number": { "type": "string" },
          "status": { "type": "string", "enum": ["scheduled","on_site","loading","to_landfill","completed","canceled"] },
          "job_date": { "type": "string", "format": "date" },
          "time_window": { "type": "object", "properties": { "start": { "type": "string", "nullable": true }, "end": { "type": "string", "nullable": true } } },
          "address": { "type": "object", "properties": { "line1": { "type": "string", "nullable": true }, "city": { "type": "string", "nullable": true }, "state": { "type": "string", "nullable": true }, "zip": { "type": "string", "nullable": true } } },
          "price": { "type": "number", "nullable": true, "description": "US dollars" },
          "payment_status": { "type": "string", "nullable": true },
          "load_fraction": { "type": "string" },
          "container_capacity_yd": { "type": "number", "nullable": true },
          "customer_ref": { "type": "string", "nullable": true },
          "completed_at": { "type": "string", "format": "date-time", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      }
    },
    "parameters": {
      "page": { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1, "minimum": 1 } },
      "per_page": { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 25, "minimum": 1, "maximum": 100 } },
      "from": { "name": "from", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Filter by created_at from this date (YYYY-MM-DD)." },
      "to": { "name": "to", "in": "query", "schema": { "type": "string", "format": "date" } }
    }
  },
  "paths": {
    "/orders": {
      "get": {
        "summary": "List orders", "operationId": "listOrders",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/from" }, { "$ref": "#/components/parameters/to" },
          { "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/per_page" }
        ],
        "responses": {
          "200": { "description": "A page of orders.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Order" } }, "page": { "type": "integer" }, "per_page": { "type": "integer" }, "has_more": { "type": "boolean" } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/AccessDisabled" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders/{order_number}": {
      "get": {
        "summary": "Get one order", "operationId": "getOrder",
        "parameters": [{ "name": "order_number", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "The order.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Order" } } } } } },
          "404": { "description": "Not found." }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/sizes": {
      "get": { "summary": "List dumpster sizes", "operationId": "listSizes", "responses": { "200": { "description": "Sizes.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Size" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }
    },
    "/customers": {
      "get": { "summary": "List customers", "operationId": "listCustomers", "parameters": [{ "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/per_page" }], "responses": { "200": { "description": "A page of customers.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Customer" } }, "has_more": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }
    },
    "/estimates": {
      "get": { "summary": "List estimates", "operationId": "listEstimates", "parameters": [{ "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/per_page" }], "responses": { "200": { "description": "A page of estimates." }, "401": { "$ref": "#/components/responses/Unauthorized" } } }
    },
    "/junk-jobs": {
      "get": { "summary": "List junk removal jobs", "operationId": "listJunkJobs", "parameters": [{ "name": "status", "in": "query", "schema": { "type": "string" } }, { "$ref": "#/components/parameters/from" }, { "$ref": "#/components/parameters/to" }, { "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/per_page" }], "responses": { "200": { "description": "A page of junk jobs.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/JunkJob" } }, "has_more": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }
    },
    "/leads": {
      "get": { "summary": "List leads", "operationId": "listLeads", "parameters": [{ "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/per_page" }], "responses": { "200": { "description": "A page of leads.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Lead" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } },
      "post": {
        "summary": "Create a lead", "operationId": "createLead",
        "description": "Push a lead into Dumpster Controls (e.g. from a GoHighLevel funnel or a website form). Does not consume the daily 10-minute window; free plan allows 200 leads/day. Duplicate phone/email within 10 minutes returns the existing lead.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string", "maxLength": 120 }, "phone": { "type": "string" }, "email": { "type": "string" }, "address": { "type": "string" }, "size_label": { "type": "string" }, "note": { "type": "string" } } } } } },
        "responses": {
          "201": { "description": "Lead created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Lead" } } } } } },
          "200": { "description": "Duplicate: existing lead returned (existing: true)." },
          "400": { "description": "Invalid body or missing name." }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "x-webhooks": {
    "description": "Configure signed webhooks in Settings, API. We POST events to your URL with header X-DC-Signature: t=<unix>,v1=<hex>, where v1 is HMAC-SHA256 of '<t>.<raw body>' using your endpoint secret. Verify it with a 5-minute timestamp tolerance. Events: order.created, order.status_changed, payment.received, lead.created, junk_job.created, junk_job.status_changed. Delivery retries with backoff up to 5 attempts."
  }
}
