{
  "openapi": "3.1.0",
  "info": {
    "title": "url2shot API",
    "version": "1.0.0",
    "description": "Async public webpage screenshots. Successful new jobs, including cache hits, cost 1 credit; exact idempotent replays do not. Images retained 24h. No paid overage."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:3000",
      "description": "Local development; replace with your connected HTTPS origin"
    }
  ],
  "security": [
    {
      "BearerKey": []
    }
  ],
  "paths": {
    "/v1/screenshots": {
      "post": {
        "operationId": "createScreenshot",
        "summary": "Reserve one credit and create or replay a capture",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[!-~]+$"
            },
            "description": "Retained 7 days; same key and different normalized input returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CaptureInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; poll job status. Cache hit may already be succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listScreenshots",
        "summary": "List up to 50 recent captures",
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Pass the previous response next_cursor unchanged. Includes creation time and job ID for stable pagination. Legacy ISO timestamps are accepted."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Job"
                      }
                    },
                    "next_cursor": {
                      "type": ["string", "null"],
                      "description": "Opaque cursor for the next page; null when exhausted."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/screenshots/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getScreenshot",
        "summary": "Get status and metadata (no download URL is minted)",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteScreenshot",
        "summary": "Revoke a completed result; no credit refund",
        "description": "A cache-shared artifact is revoked for every job referencing it within this account. Previously issued remote URLs can survive until object deletion or their 5-minute expiry.",
        "responses": {
          "204": {
            "description": "Deleted or already deleted"
          },
          "400": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/screenshots/{id}/download-url": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "createDownloadUrl",
        "summary": "Create a signed image URL (maximum 5 minutes)",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "url_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Read current entitlement and reserved/consumed credits",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "limit": {
                      "type": "integer"
                    },
                    "consumed": {
                      "type": "integer"
                    },
                    "reserved": {
                      "type": "integer"
                    },
                    "available": {
                      "type": "integer"
                    },
                    "period": {
                      "type": ["object", "null"]
                    },
                    "totals": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Structured error; honor Retry-After on 429/503",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Issue and revoke API keys in the authenticated dashboard."
      }
    },
    "schemas": {
      "CaptureInput": {
        "type": "object",
        "additionalProperties": false,
        "required": ["url"],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public HTTP(S) domain; at most 2048 UTF-8 bytes. No credentials, literal IP or nonstandard port."
          },
          "viewport": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "width": {
                "type": "integer",
                "minimum": 320,
                "maximum": 1920,
                "default": 1280
              },
              "height": {
                "type": "integer",
                "minimum": 240,
                "maximum": 1080,
                "default": 720
              }
            }
          },
          "format": {
            "enum": ["png", "jpeg"],
            "default": "png"
          },
          "quality": {
            "type": "integer",
            "minimum": 40,
            "maximum": 90,
            "description": "JPEG only. Defaults to 80; invalid with PNG."
          },
          "full_page": {
            "type": "boolean",
            "default": false,
            "description": "Top 6000px maximum; returns PAGE_TRUNCATED if longer."
          },
          "wait_ms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2000,
            "default": 500
          },
          "cache_ttl_seconds": {
            "type": "integer",
            "minimum": 0,
            "maximum": 300,
            "default": 300
          }
        }
      },
      "Job": {
        "type": "object",
        "required": ["id", "status", "usage", "warnings", "result"],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "enum": ["queued", "running", "retry_wait", "succeeded", "failed", "expired"]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "usage": {
            "type": "object",
            "properties": {
              "reserved": {
                "type": "integer"
              },
              "charged": {
                "type": "integer"
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "capture": {
            "type": "object"
          },
          "error": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ErrorDetail"
              },
              {
                "type": "null"
              }
            ]
          },
          "artifact_status": {
            "enum": ["available", "expired", null]
          },
          "result": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Artifact"
              },
              {
                "type": "null"
              }
            ]
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "Artifact": {
        "type": "object",
        "properties": {
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "bytes": {
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "format": {
            "enum": ["png", "jpeg"]
          },
          "captured_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "cache_hit": {
            "type": "boolean"
          }
        }
      },
      "ErrorDetail": {
        "type": "object",
        "required": ["code", "message", "retryable"],
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error", "request_id"],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          },
          "request_id": {
            "type": "string"
          }
        }
      }
    }
  }
}
