{
  "openapi": "3.1.0",
  "info": {
    "title": "Subdora Agent API",
    "version": "1.0.0",
    "description": "Scoped API for coding agents to claim and manage Subdora URLs for apps that are already deployed to public HTTPS origins."
  },
  "servers": [{ "url": "https://subdora.com" }],
  "externalDocs": {
    "description": "Subdora agent skill",
    "url": "https://subdora.com/agents/subdora/SKILL.md"
  },
  "tags": [
    { "name": "Authorization" },
    { "name": "Account" },
    { "name": "Names" },
    { "name": "Targets" }
  ],
  "paths": {
    "/api/agent/device/start": {
      "post": {
        "tags": ["Authorization"],
        "operationId": "startDeviceAuthorization",
        "summary": "Start human authorization for a coding agent",
        "security": [],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceStart" } } }
        },
        "responses": {
          "201": {
            "description": "Authorization request created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceAuthorization" } } }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/agent/device/token": {
      "post": {
        "tags": ["Authorization"],
        "operationId": "exchangeDeviceCode",
        "summary": "Poll for a scoped access token",
        "description": "Poll no faster than the interval returned by startDeviceAuthorization. HTTP 428 authorization_pending is expected before approval.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceTokenRequest" } } }
        },
        "responses": {
          "200": {
            "description": "Token issued once",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentToken" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "428": {
            "description": "Human approval is still pending",
            "headers": { "Retry-After": { "schema": { "type": "integer" } } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/api/agent/v1/account": {
      "get": {
        "tags": ["Account"],
        "operationId": "getAgentAccount",
        "summary": "Read account and Forever Free slot usage",
        "responses": {
          "200": {
            "description": "Account summary",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountSummary" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/agent/v1/subdomains": {
      "get": {
        "tags": ["Account"],
        "operationId": "listSubdomains",
        "summary": "List the authorized user's Subdora names",
        "responses": {
          "200": {
            "description": "Subdomains",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["subdomains"],
                  "properties": { "subdomains": { "type": "array", "items": { "$ref": "#/components/schemas/Subdomain" } } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/agent/v1/subdomains/{id}": {
      "get": {
        "tags": ["Account"],
        "operationId": "getSubdomain",
        "summary": "Read a subdomain with revisions and recent health checks",
        "parameters": [{ "$ref": "#/components/parameters/SubdomainId" }],
        "responses": {
          "200": {
            "description": "Subdomain detail",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubdomainDetail" } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/names/{label}/availability": {
      "get": {
        "tags": ["Names"],
        "operationId": "checkNameAvailability",
        "summary": "Check the exact requested label",
        "parameters": [{ "$ref": "#/components/parameters/Label" }],
        "responses": {
          "200": {
            "description": "Availability result",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Availability" } } }
          }
        }
      }
    },
    "/api/bookings/quote": {
      "post": {
        "tags": ["Names"],
        "operationId": "quoteBooking",
        "summary": "Quote a free or credit-funded booking before user approval",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingQuoteInput" } } }
        },
        "responses": {
          "200": {
            "description": "Booking quote",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingQuote" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/bookings": {
      "post": {
        "tags": ["Names"],
        "operationId": "createBooking",
        "summary": "Claim a Subdora name after any required user approval",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBookingInput" } } }
        },
        "responses": {
          "201": {
            "description": "Name claimed and awaiting origin verification",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } }
          },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/api/subdomains/{id}/verify": {
      "post": {
        "tags": ["Targets"],
        "operationId": "verifyAndPublish",
        "summary": "Verify the server-delivered meta tag and publish the route",
        "parameters": [
          { "$ref": "#/components/parameters/SubdomainId" },
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "responses": {
          "200": {
            "description": "Published",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerificationResult" } } }
          },
          "422": { "$ref": "#/components/responses/Unprocessable" }
        }
      }
    },
    "/api/subdomains/{id}/health": {
      "post": {
        "tags": ["Targets"],
        "operationId": "runHealthCheck",
        "summary": "Check target HTTPS, redirects, nested routes, assets, and common auth errors",
        "parameters": [
          { "$ref": "#/components/parameters/SubdomainId" },
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "responses": {
          "200": {
            "description": "Health result",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthCheck" } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/subdomains/{id}/target": {
      "post": {
        "tags": ["Targets"],
        "operationId": "changeTarget",
        "summary": "Point a name at a new deployment and issue fresh ownership proof",
        "parameters": [
          { "$ref": "#/components/parameters/SubdomainId" },
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TargetInput" } } }
        },
        "responses": {
          "200": {
            "description": "Target changed and awaiting verification",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TargetChange" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/subdomains/{id}/revisions/{revisionId}/rollback": {
      "post": {
        "tags": ["Targets"],
        "operationId": "rollbackTarget",
        "summary": "Restore a previously verified target revision",
        "parameters": [
          { "$ref": "#/components/parameters/SubdomainId" },
          { "$ref": "#/components/parameters/RevisionId" },
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "responses": {
          "200": {
            "description": "Rollback completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["ok", "targetUrl"],
                  "properties": { "ok": { "const": true }, "targetUrl": { "type": "string", "format": "uri" } }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "agentBearer": { "type": "http", "scheme": "bearer", "bearerFormat": "Subdora agent token" }
    },
    "parameters": {
      "SubdomainId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
      "RevisionId": { "name": "revisionId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
      "Label": { "name": "label", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/Label" } },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique stable key for this exact write, 8-128 URL-safe characters.",
        "schema": { "type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[A-Za-z0-9._:-]+$" }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string" } }
      },
      "Label": { "type": "string", "minLength": 2, "maxLength": 63, "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$" },
      "Provider": { "type": "string", "enum": ["lovable", "vercel", "netlify", "cloudflare", "replit", "github", "custom"] },
      "Scopes": { "type": "array", "items": { "type": "string", "enum": ["names:read", "names:write", "targets:write", "health:write"] } },
      "DeviceStart": {
        "type": "object",
        "properties": { "clientName": { "type": "string", "minLength": 2, "maxLength": 80, "default": "Coding agent" } }
      },
      "DeviceAuthorization": {
        "type": "object",
        "required": ["deviceCode", "userCode", "verificationUri", "verificationUriComplete", "expiresIn", "interval"],
        "properties": {
          "deviceCode": { "type": "string" },
          "userCode": { "type": "string" },
          "verificationUri": { "type": "string", "format": "uri" },
          "verificationUriComplete": { "type": "string", "format": "uri" },
          "expiresIn": { "type": "integer", "example": 600 },
          "interval": { "type": "integer", "example": 5 }
        }
      },
      "DeviceTokenRequest": {
        "type": "object",
        "required": ["deviceCode"],
        "properties": { "deviceCode": { "type": "string", "minLength": 32, "maxLength": 200 } }
      },
      "AgentToken": {
        "type": "object",
        "required": ["accessToken", "tokenType", "expiresAt", "scopes"],
        "properties": {
          "accessToken": { "type": "string", "writeOnly": true },
          "tokenType": { "const": "Bearer" },
          "expiresAt": { "type": "string", "format": "date-time" },
          "scopes": { "$ref": "#/components/schemas/Scopes" }
        }
      },
      "AccountSummary": {
        "type": "object",
        "required": ["account", "foreverFreeSlots", "scopes"],
        "properties": {
          "account": {
            "type": "object",
            "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "email": { "type": "string", "format": "email" } }
          },
          "foreverFreeSlots": {
            "type": "object",
            "required": ["used", "total"],
            "properties": { "used": { "type": "integer" }, "total": { "type": "integer", "const": 5 } }
          },
          "scopes": { "$ref": "#/components/schemas/Scopes" }
        }
      },
      "Subdomain": {
        "type": "object",
        "required": ["id", "label", "fqdn", "url", "status", "routeMode", "foreverFree", "targetUrl", "provider", "verificationToken", "createdAt", "updatedAt"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "label": { "$ref": "#/components/schemas/Label" },
          "fqdn": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "status": { "type": "string" },
          "routeMode": { "type": "string" },
          "foreverFree": { "type": "boolean" },
          "targetUrl": { "type": "string", "format": "uri" },
          "provider": { "$ref": "#/components/schemas/Provider" },
          "verificationToken": { "type": "string" },
          "bookedUntil": { "type": ["string", "null"], "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "Revision": {
        "type": "object",
        "required": ["id", "targetUrl", "provider", "active", "createdAt"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "targetUrl": { "type": "string", "format": "uri" },
          "provider": { "$ref": "#/components/schemas/Provider" },
          "active": { "type": "boolean" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "SubdomainDetail": {
        "type": "object",
        "required": ["subdomain", "revisions", "healthChecks"],
        "properties": {
          "subdomain": { "$ref": "#/components/schemas/Subdomain" },
          "revisions": { "type": "array", "items": { "$ref": "#/components/schemas/Revision" } },
          "healthChecks": { "type": "array", "items": { "$ref": "#/components/schemas/HealthCheck" } }
        }
      },
      "Availability": {
        "type": "object",
        "required": ["available", "reason", "fqdn"],
        "properties": {
          "available": { "type": "boolean" },
          "reason": { "type": ["string", "null"], "enum": ["invalid", "reserved", "taken", null] },
          "fqdn": { "type": "string" }
        }
      },
      "BookingQuoteInput": {
        "type": "object",
        "required": ["label", "months", "foreverFree"],
        "properties": {
          "label": { "$ref": "#/components/schemas/Label" },
          "months": { "type": "integer", "minimum": 1, "maximum": 120 },
          "foreverFree": { "type": "boolean" }
        }
      },
      "BookingQuote": {
        "allOf": [
          { "$ref": "#/components/schemas/BookingQuoteInput" },
          {
            "type": "object",
            "required": ["credits", "bookedUntil"],
            "properties": {
              "credits": { "type": "integer", "minimum": 0 },
              "bookedUntil": { "type": ["string", "null"], "format": "date-time" }
            }
          }
        ]
      },
      "TargetInput": {
        "type": "object",
        "required": ["targetUrl", "provider"],
        "properties": {
          "targetUrl": { "type": "string", "format": "uri", "pattern": "^https://" },
          "provider": { "$ref": "#/components/schemas/Provider" }
        }
      },
      "CreateBookingInput": {
        "allOf": [
          { "$ref": "#/components/schemas/BookingQuoteInput" },
          { "$ref": "#/components/schemas/TargetInput" }
        ]
      },
      "Booking": {
        "type": "object",
        "required": ["id", "label", "fqdn", "targetUrl", "verificationToken", "status", "creditsSpent", "bookedUntil"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "label": { "$ref": "#/components/schemas/Label" },
          "fqdn": { "type": "string" },
          "targetUrl": { "type": "string", "format": "uri" },
          "verificationToken": { "type": "string" },
          "status": { "const": "verifying" },
          "creditsSpent": { "type": "integer" },
          "bookedUntil": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "VerificationResult": {
        "type": "object",
        "required": ["verified", "status", "fqdn"],
        "properties": {
          "verified": { "const": true },
          "status": { "const": "live" },
          "fqdn": { "type": "string" }
        }
      },
      "HealthCheck": {
        "type": "object",
        "required": ["status", "latencyMs", "https", "redirects", "nestedRoutes", "assets", "message"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "status": { "type": "string", "enum": ["healthy", "warning", "error"] },
          "latencyMs": { "type": "integer" },
          "https": { "type": "boolean" },
          "redirects": { "type": "integer" },
          "nestedRoutes": { "type": "boolean" },
          "assets": { "type": "boolean" },
          "authWarning": { "type": "boolean" },
          "message": { "type": "string" },
          "checkedAt": { "type": "string", "format": "date-time" }
        }
      },
      "TargetChange": {
        "type": "object",
        "required": ["targetUrl", "provider", "status", "verificationToken"],
        "properties": {
          "targetUrl": { "type": "string", "format": "uri" },
          "provider": { "$ref": "#/components/schemas/Provider" },
          "status": { "const": "verifying" },
          "verificationToken": { "type": "string" }
        }
      }
    },
    "responses": {
      "BadRequest": { "description": "Invalid input", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "Agent token missing, invalid, expired, or revoked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden": { "description": "Scope or policy restriction", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "PaymentRequired": { "description": "Insufficient purchased credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Conflict": { "description": "Name, slot, race, or idempotency conflict", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unprocessable": { "description": "Verification or target validation failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": {
        "description": "Too many requests",
        "headers": { "Retry-After": { "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  },
  "security": [{ "agentBearer": [] }]
}
