{
 "openapi": "3.1.0",
 "info": {
  "title": "Ottobrowse",
  "version": "1.0.0",
  "description": "Disposable Chrome sessions in their own microVM, driven over CDP. Every endpoint requires a project API key; sign up and create one at /browser."
 },
 "servers": [
  {
   "url": "/api/browser/v1"
  }
 ],
 "security": [
  {
   "apiKey": []
  }
 ],
 "paths": {
  "/sessions": {
   "post": {
    "operationId": "createSession",
    "summary": "Create or replay a browser session",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[A-Za-z0-9_-]{8,128}$"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
         "timeoutSeconds": {
          "type": "integer",
          "minimum": 60,
          "maximum": 21600,
          "default": 900,
          "description": "Browser lifetime. The ceiling is six hours; your plan may allow less \u2014 GET /usage reports the effective maximum."
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Existing reservation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Session"
        }
       }
      }
     },
     "201": {
      "description": "Created",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Session"
        }
       }
      }
     },
     "202": {
      "description": "Still starting; poll session",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Session"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "No active plan for this project",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Session not found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "Idempotency conflict",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "Body too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "415": {
      "description": "Unsupported content type",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Concurrency, daily or included-hours limit reached",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "Unavailable; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "get": {
    "operationId": "listSessions",
    "summary": "List project sessions without connection credentials",
    "parameters": [
     {
      "name": "offset",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "maximum": 100000,
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Page",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "sessions": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Session"
           }
          },
          "nextOffset": {
           "type": [
            "integer",
            "null"
           ]
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "No active plan for this project",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Session not found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "Idempotency conflict",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "Body too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "415": {
      "description": "Unsupported content type",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Quota exhausted; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "Unavailable; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/sessions/{id}": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string",
      "format": "uuid"
     }
    }
   ],
   "get": {
    "operationId": "getSession",
    "summary": "Get owned session and fresh live-view link",
    "responses": {
     "200": {
      "description": "Session",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Session"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "No active plan for this project",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Session not found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "Idempotency conflict",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "Body too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "415": {
      "description": "Unsupported content type",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Quota exhausted; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "Unavailable; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "delete": {
    "operationId": "stopSession",
    "summary": "Stop a session; safe to retry",
    "responses": {
     "200": {
      "description": "Stopped",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Session"
        }
       }
      }
     },
     "202": {
      "description": "Startup cancellation pending; retry DELETE",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Session"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "No active plan for this project",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Session not found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "Idempotency conflict",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "Body too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "415": {
      "description": "Unsupported content type",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Quota exhausted; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "Unavailable; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/usage": {
   "get": {
    "operationId": "getUsage",
    "summary": "Plan, billing-period usage and effective limits",
    "responses": {
     "200": {
      "description": "Usage",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "plan": {
           "type": "string",
           "enum": [
            "free",
            "dev",
            "scale"
           ]
          },
          "period": {
           "type": "object",
           "description": "The current Stripe billing period and what it has spent.",
           "properties": {
            "start": {
             "type": "string",
             "format": "date-time"
            },
            "end": {
             "type": "string",
             "format": "date-time"
            },
            "browserSeconds": {
             "type": "integer",
             "description": "Browser time used this period."
            },
            "includedSeconds": {
             "type": "integer",
             "description": "Browser time the plan includes."
            },
            "overageSeconds": {
             "type": "integer",
             "description": "Used beyond the included allowance."
            },
            "overageBilled": {
             "type": "boolean",
             "description": "False on plans that stop instead of charging."
            }
           }
          },
          "windowStart": {
           "type": "string",
           "format": "date-time"
          },
          "sessionsLast24Hours": {
           "type": "integer"
          },
          "activeSessions": {
           "type": "integer"
          },
          "limits": {
           "type": "object",
           "properties": {
            "maxConcurrent": {
             "type": "integer"
            },
            "dailySessions": {
             "type": "integer"
            },
            "maxSessionSeconds": {
             "type": "integer"
            }
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "No active plan for this project",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Session not found",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "409": {
      "description": "Idempotency conflict",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "Body too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "415": {
      "description": "Unsupported content type",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Quota exhausted; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "503": {
      "description": "Unavailable; see Retry-After",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "apiKey": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "ob_<32 random bytes, base64url>"
   }
  },
  "schemas": {
   "Session": {
    "type": "object",
    "required": [
     "id",
     "status",
     "expiresAt",
     "timeoutSeconds"
    ],
    "properties": {
     "id": {
      "type": "string",
      "format": "uuid"
     },
     "status": {
      "type": "string",
      "enum": [
       "starting",
       "running",
       "stopping",
       "stopped",
       "expired",
       "failed"
      ]
     },
     "createdAt": {
      "type": "string",
      "format": "date-time"
     },
     "startedAt": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "expiresAt": {
      "type": "string",
      "format": "date-time"
     },
     "endedAt": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "timeoutSeconds": {
      "type": "integer"
     },
     "connectUrl": {
      "type": "string",
      "format": "uri",
      "description": "Secret automation capability, only present on running session detail."
     },
     "liveViewUrl": {
      "type": "string",
      "format": "uri",
      "description": "Secret live-view capability, expires within 10 minutes."
     }
    }
   },
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string"
       },
       "message": {
        "type": "string"
       },
       "requestId": {
        "type": "string",
        "format": "uuid"
       }
      }
     }
    }
   }
  }
 }
}
