{
  "openapi": "3.0.3",
  "info": {
    "title": "PwrOn Agent API",
    "description": "API for AI agents to discover and book portable power stations for delivery. No authentication required. See https://pwron.com/llms.txt for human-readable docs.",
    "version": "1.0.0",
    "contact": {
      "email": "api@pwron.com"
    }
  },
  "servers": [
    {
      "url": "https://pwron.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/agent/v1/catalog/devices.json": {
      "get": {
        "operationId": "getDeviceCatalog",
        "summary": "Get full device catalog",
        "description": "Static JSON listing all devices available for delivery. Check this first to see if devices exist near a location before making search calls. Refreshed daily, CDN-cached.",
        "responses": {
          "200": {
            "description": "Device catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceCatalogResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/v1/devices/search": {
      "get": {
        "operationId": "searchDevices",
        "summary": "Search for available devices with pricing",
        "description": "Find available devices that can deliver to a location for specific dates. Returns devices with full pricing breakdown.",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Delivery latitude",
            "schema": { "type": "number", "minimum": -90, "maximum": 90 }
          },
          {
            "name": "lng",
            "in": "query",
            "required": true,
            "description": "Delivery longitude",
            "schema": { "type": "number", "minimum": -180, "maximum": 180 }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": true,
            "description": "Booking start (ISO 8601 datetime)",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": true,
            "description": "Booking end (ISO 8601 datetime)",
            "schema": { "type": "string", "format": "date-time" }
          },
          {
            "name": "class",
            "in": "query",
            "required": false,
            "description": "Device class filter",
            "schema": { "type": "string", "enum": ["Micro", "Small", "Medium", "Large"] }
          },
          {
            "name": "min_capacity_wh",
            "in": "query",
            "required": false,
            "description": "Minimum battery capacity in Wh",
            "schema": { "type": "number", "minimum": 0 }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per page (default 20, max 50)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results with pricing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/v1/bookings/hold": {
      "post": {
        "operationId": "createHold",
        "summary": "Create a hold on a device",
        "description": "Reserve a device for 12 hours. The user receives an email with a payment link. No payment = no booking. No authentication required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HoldRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Hold created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HoldResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (bad email, dates, or address)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Device not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Device unavailable for requested dates",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or too many active holds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/v1/holds/{holdId}": {
      "get": {
        "operationId": "getHoldStatus",
        "summary": "Check hold status",
        "description": "Poll to check if the user completed payment. Status progresses from awaiting_payment to confirmed or expired.",
        "parameters": [
          {
            "name": "holdId",
            "in": "path",
            "required": true,
            "description": "Hold ID returned from createHold",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Hold status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HoldStatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "Hold not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "DeviceCatalogResponse": {
        "type": "object",
        "required": ["generated_at", "expires_at", "device_count", "devices"],
        "properties": {
          "generated_at": { "type": "string", "format": "date-time" },
          "expires_at": { "type": "string", "format": "date-time" },
          "device_count": { "type": "integer" },
          "devices": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogDevice" }
          }
        }
      },
      "CatalogDevice": {
        "type": "object",
        "required": ["id", "brand", "model", "class", "capacity_wh", "output_w", "daily_rate", "willing_to_deliver", "lat", "lng"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "brand": { "type": "string" },
          "model": { "type": "string" },
          "class": { "type": "string", "enum": ["Micro", "Small", "Medium", "Large"] },
          "capacity_wh": { "type": "number", "description": "Battery capacity in watt-hours" },
          "output_w": { "type": "number", "description": "Max output in watts" },
          "daily_rate": { "type": "number", "description": "Price per day in USD" },
          "daily_rate_total_at_7_days": { "type": "number", "nullable": true, "description": "Total price for 7-day booking in USD" },
          "daily_rate_total_at_30_days": { "type": "number", "nullable": true, "description": "Total price for 30-day booking in USD" },
          "pickup_enabled": { "type": "boolean" },
          "willing_to_deliver": { "type": "boolean" },
          "max_delivery_distance": { "type": "number", "nullable": true, "description": "Max delivery distance in miles" },
          "lat": { "type": "number", "description": "Device latitude (rounded to ~1km)" },
          "lng": { "type": "number", "description": "Device longitude (rounded to ~1km)" }
        }
      },
      "DeviceSearchResponse": {
        "type": "object",
        "required": ["devices", "total_count", "delivery_location", "dates"],
        "properties": {
          "devices": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/DeviceSearchResult" }
          },
          "total_count": { "type": "integer" },
          "delivery_location": {
            "type": "object",
            "required": ["lat", "lng"],
            "properties": {
              "lat": { "type": "number" },
              "lng": { "type": "number" }
            }
          },
          "dates": {
            "type": "object",
            "required": ["start", "end"],
            "properties": {
              "start": { "type": "string", "format": "date-time" },
              "end": { "type": "string", "format": "date-time" }
            }
          }
        }
      },
      "DeviceSearchResult": {
        "type": "object",
        "required": ["id", "brand", "model", "class", "capacity_wh", "output_w", "daily_rate", "delivery_distance_miles", "booking_days", "booking_price", "delivery_fee", "platform_fee", "customer_total"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "brand": { "type": "string" },
          "model": { "type": "string" },
          "class": { "type": "string", "enum": ["Micro", "Small", "Medium", "Large"] },
          "capacity_wh": { "type": "number" },
          "output_w": { "type": "number" },
          "daily_rate": { "type": "number", "description": "Price per day in USD" },
          "daily_rate_total_at_7_days": { "type": "number", "nullable": true },
          "daily_rate_total_at_30_days": { "type": "number", "nullable": true },
          "delivery_distance_miles": { "type": "number" },
          "booking_days": { "type": "integer" },
          "booking_price": { "type": "number", "description": "Rental subtotal in USD" },
          "delivery_fee": { "type": "number", "description": "Delivery fee in USD" },
          "platform_fee": { "type": "number", "description": "Platform fee in USD" },
          "customer_total": { "type": "number", "description": "Total customer pays in USD" }
        }
      },
      "HoldRequest": {
        "type": "object",
        "required": ["device_id", "user_email", "start_date", "end_date", "delivery_address"],
        "properties": {
          "device_id": { "type": "string", "format": "uuid", "description": "Device ID from search results" },
          "user_email": { "type": "string", "format": "email", "description": "Email of user who will receive payment link" },
          "start_date": { "type": "string", "format": "date-time" },
          "end_date": { "type": "string", "format": "date-time" },
          "delivery_address": { "type": "string", "description": "Street address for delivery" },
          "webhook_url": { "type": "string", "format": "uri", "description": "URL for booking status webhooks" },
          "agent_metadata": {
            "$ref": "#/components/schemas/AgentMetadata"
          }
        }
      },
      "AgentMetadata": {
        "type": "object",
        "description": "Optional metadata about the agent creating the hold",
        "properties": {
          "agent_name": { "type": "string", "maxLength": 100, "description": "Display name shown to user in email" },
          "agent_comment": { "type": "string", "maxLength": 500, "description": "Reason for booking shown to user in email" }
        },
        "additionalProperties": true
      },
      "HoldResponse": {
        "type": "object",
        "required": ["hold_id", "status", "expires_at", "email_sent", "booking_days", "booking_price", "delivery_fee", "platform_fee", "customer_total", "device"],
        "properties": {
          "hold_id": { "type": "string", "format": "uuid" },
          "status": { "type": "string", "enum": ["awaiting_payment"] },
          "expires_at": { "type": "string", "format": "date-time" },
          "email_sent": { "type": "boolean" },
          "booking_days": { "type": "integer" },
          "booking_price": { "type": "number" },
          "delivery_fee": { "type": "number" },
          "platform_fee": { "type": "number" },
          "customer_total": { "type": "number" },
          "device": { "$ref": "#/components/schemas/DeviceInfo" }
        }
      },
      "HoldStatusResponse": {
        "type": "object",
        "required": ["hold_id", "status", "expires_at", "device_id", "start_date", "end_date", "delivery_address", "booking_days", "booking_price", "delivery_fee", "platform_fee", "customer_total"],
        "properties": {
          "hold_id": { "type": "string", "format": "uuid" },
          "status": { "type": "string", "enum": ["awaiting_payment", "confirmed", "expired"] },
          "expires_at": { "type": "string", "format": "date-time" },
          "device_id": { "type": "string", "format": "uuid" },
          "start_date": { "type": "string", "format": "date-time" },
          "end_date": { "type": "string", "format": "date-time" },
          "delivery_address": { "type": "string" },
          "delivery_distance_miles": { "type": "number", "nullable": true },
          "booking_days": { "type": "integer" },
          "booking_price": { "type": "number" },
          "delivery_fee": { "type": "number" },
          "platform_fee": { "type": "number" },
          "customer_total": { "type": "number" },
          "device": {
            "nullable": true,
            "allOf": [{ "$ref": "#/components/schemas/DeviceInfo" }]
          }
        }
      },
      "DeviceInfo": {
        "type": "object",
        "required": ["id", "brand", "model", "class", "capacity_wh", "output_w"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "brand": { "type": "string" },
          "model": { "type": "string" },
          "class": { "type": "string", "enum": ["Micro", "Small", "Medium", "Large"] },
          "capacity_wh": { "type": "number" },
          "output_w": { "type": "number" }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": ["invalid_email", "disposable_email", "device_not_found", "device_unavailable", "invalid_dates", "invalid_location", "invalid_request", "rate_limit_exceeded", "too_many_holds", "hold_not_found"]
              },
              "message": { "type": "string" }
            }
          }
        }
      }
    }
  }
}
