{
  "info": {
    "title": "Clonesite AI Public Discovery API",
    "version": "0.1.0",
    "description": "Public discovery surface for Clonesite AI. The Clone API is public for external agents with a human-created API key. Clone request creation costs 5 credits and is automatically refunded if the clone fails; the first source ZIP unlock costs 100 credits. API keys are rate-limited to 60 requests per minute."
  },
  "servers": [
    {
      "url": "https://clonesite.ai",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Human-created Clonesite API key from the /developers dashboard. Agents cannot self-register or obtain keys."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Required idempotency key for safe retries. Maximum 128 characters. Allowed characters: A-Z, a-z, 0-9, dot, underscore, colon, and hyphen.",
        "schema": {
          "type": "string",
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9._:-]+$"
        }
      },
      "CloneRequestId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Public clone request id returned by POST /api/v1/clone-requests.",
        "schema": {
          "type": "string",
          "pattern": "^api_req_[A-Za-z0-9]+$"
        },
        "example": "api_req_0123456789abcdef01234567"
      },
      "WebhookTimestamp": {
        "name": "X-Clonesite-Timestamp",
        "in": "header",
        "required": true,
        "description": "Unix timestamp in seconds used in the webhook signature base string.",
        "schema": {
          "type": "string"
        },
        "example": "1781946000"
      },
      "WebhookSignature": {
        "name": "X-Clonesite-Signature",
        "in": "header",
        "required": true,
        "description": "Webhook HMAC signature: v1=hex(hmac_sha256(secret, timestamp + \".\" + rawBody)).",
        "schema": {
          "type": "string",
          "pattern": "^v1=[0-9a-f]+$"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request body or idempotency key. Codes include missing_idempotency_key, invalid_json, and invalid_request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key. Codes include missing_api_key and invalid_api_key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Insufficient credits. Code: insufficient_credits.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "API key account is unavailable or lacks permission. Codes include api_key_account_not_found and insufficient_permissions.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "Clone request not found. Code: not_found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "State or idempotency conflict. Codes include idempotency_conflict, request_not_ready, and source_artifact_not_ready.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Valid JSON with invalid callback URL configuration. Codes include invalid_callback_url and webhook_not_configured.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "API key rate or usage limit exceeded. Codes include rate_limited and usage_exceeded.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying when provided by the key verifier.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error. Code: internal_error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "CreateCloneRequest": {
        "type": "object",
        "properties": {
          "sourceUrl": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "format": "uri",
            "description": "Public website URL to clone. Must use http or https."
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000,
            "description": "Instructions for how Clonesite should recreate and adapt the source site."
          },
          "externalRequestId": {
            "type": "string",
            "maxLength": 256,
            "description": "Optional client-side request id for correlation."
          },
          "callbackUrl": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "Optional HTTPS webhook URL. Must use https, no credentials, port 443 only, and no localhost or private IP hosts. Requires active API webhook settings."
          }
        },
        "required": [
          "sourceUrl",
          "prompt"
        ],
        "additionalProperties": false
      },
      "CreateCloneTestRun": {
        "type": "object",
        "properties": {
          "sourceUrl": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "format": "uri",
            "description": "Public website URL used to validate the same request shape as a live clone."
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000,
            "description": "Instructions used to validate the same request shape as a live clone."
          },
          "externalRequestId": {
            "type": "string",
            "maxLength": 256,
            "description": "Optional client-side request id for correlation."
          },
          "callbackUrl": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "Optional HTTPS webhook URL. Test runs send a real signed webhook when this is present and webhook settings are configured."
          },
          "scenario": {
            "anyOf": [
              {
                "const": "ready"
              },
              {
                "const": "failed"
              }
            ],
            "description": "Mock terminal scenario. ready exercises success; failed exercises failure handling."
          }
        },
        "required": [
          "sourceUrl",
          "prompt"
        ],
        "additionalProperties": false
      },
      "CloneRequestPreflight": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "mode": {
            "const": "preflight"
          },
          "canCreate": {
            "type": "boolean",
            "description": "True when the same API key and payload can be submitted to the live create endpoint right now."
          },
          "wouldChargeCredits": {
            "type": "number",
            "description": "Credits that would be charged by the live create endpoint."
          },
          "wouldCreateCloneRequest": {
            "const": false,
            "description": "Always false; preflight has no database side effects."
          },
          "wouldCreateJob": {
            "const": false,
            "description": "Always false; preflight never queues generation."
          },
          "checks": {
            "type": "object",
            "properties": {
              "apiKey": {
                "const": "valid"
              },
              "permission": {
                "const": "clone_requests:create"
              },
              "payload": {
                "const": "valid"
              },
              "credits": {
                "anyOf": [
                  {
                    "const": "sufficient"
                  },
                  {
                    "const": "insufficient"
                  }
                ]
              },
              "webhook": {
                "anyOf": [
                  {
                    "const": "not_requested"
                  },
                  {
                    "const": "configured"
                  },
                  {
                    "const": "not_configured"
                  }
                ]
              }
            },
            "required": [
              "apiKey",
              "permission",
              "payload",
              "credits",
              "webhook"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "ok",
          "mode",
          "canCreate",
          "wouldChargeCredits",
          "wouldCreateCloneRequest",
          "wouldCreateJob",
          "checks"
        ],
        "additionalProperties": false
      },
      "CloneRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^api_req_[A-Za-z0-9]+$",
            "description": "Public API clone request id.",
            "example": "api_req_0123456789abcdef01234567"
          },
          "mode": {
            "description": "live for real billable clone requests; test for free mock integration runs.",
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "externalRequestId": {
            "type": "string",
            "description": "Client correlation id if supplied."
          },
          "status": {
            "description": "API-visible clone state. New POST responses normally return queued.",
            "type": "string",
            "enum": [
              "accepted",
              "queued",
              "processing",
              "ready",
              "failed"
            ]
          },
          "sourceUrl": {
            "type": "string",
            "format": "uri"
          },
          "templateSlug": {
            "type": "string"
          },
          "jobId": {
            "type": "string"
          },
          "previewUrl": {
            "type": "string",
            "format": "uri"
          },
          "error": {
            "type": "string"
          },
          "credits": {
            "$ref": "#/components/schemas/CloneRequestCredits"
          },
          "sourceZip": {
            "$ref": "#/components/schemas/CloneRequestSourceZip"
          },
          "webhook": {
            "$ref": "#/components/schemas/CloneRequestWebhook"
          },
          "createdAt": {
            "type": "number",
            "description": "Unix epoch timestamp in milliseconds."
          },
          "updatedAt": {
            "type": "number",
            "description": "Unix epoch timestamp in milliseconds."
          },
          "statusUrl": {
            "type": "string",
            "description": "Path to poll for this clone request."
          }
        },
        "required": [
          "id",
          "mode",
          "status",
          "sourceUrl",
          "credits",
          "sourceZip",
          "webhook",
          "createdAt",
          "updatedAt",
          "statusUrl"
        ],
        "additionalProperties": false
      },
      "CloneRequestCredits": {
        "type": "object",
        "properties": {
          "charged": {
            "type": "boolean"
          },
          "creditCost": {
            "type": "number",
            "description": "Credits charged for this request. Live clone requests cost 5; test runs cost 0."
          },
          "refunded": {
            "type": "boolean",
            "description": "True after a failed clone request has been refunded."
          }
        },
        "required": [
          "charged",
          "creditCost",
          "refunded"
        ],
        "additionalProperties": false
      },
      "CloneRequestSourceZip": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean",
            "description": "True when the clone is ready and a source artifact exists."
          },
          "unlocked": {
            "type": "boolean",
            "description": "True when the account already has source-download entitlement for this template."
          },
          "creditCost": {
            "type": "number",
            "description": "Credits charged for first source ZIP unlock. Live source ZIP unlocks cost 100; test fixture downloads cost 0."
          }
        },
        "required": [
          "available",
          "unlocked",
          "creditCost"
        ],
        "additionalProperties": false
      },
      "CloneRequestWebhook": {
        "type": "object",
        "properties": {
          "lastStatus": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "delivered",
              "failed"
            ]
          }
        },
        "required": [
          "lastStatus"
        ],
        "additionalProperties": false
      },
      "SourceDownload": {
        "type": "object",
        "properties": {
          "downloadUrl": {
            "type": "string",
            "format": "uri",
            "description": "Short-lived signed URL for the ZIP file."
          },
          "expiresAt": {
            "type": "number",
            "description": "Unix epoch timestamp in milliseconds. Signed URLs currently expire after about 5 minutes."
          },
          "filename": {
            "type": "string"
          },
          "creditCost": {
            "type": "number",
            "description": "Credits charged for this download. Live first unlock costs 100; test fixture downloads cost 0."
          },
          "alreadyUnlocked": {
            "type": "boolean",
            "description": "False when this request performed the first paid unlock; true when entitlement already existed."
          },
          "artifact": {
            "$ref": "#/components/schemas/SourceArtifact"
          }
        },
        "required": [
          "downloadUrl",
          "expiresAt",
          "filename",
          "creditCost",
          "alreadyUnlocked",
          "artifact"
        ],
        "additionalProperties": false
      },
      "SourceArtifact": {
        "type": "object",
        "properties": {
          "artifactId": {
            "type": "string"
          },
          "templateSlug": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "contentType": {
            "type": "string",
            "example": "application/zip"
          },
          "status": {
            "type": "string"
          },
          "checksumSha256": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "number"
          },
          "createdAt": {
            "type": "number"
          },
          "updatedAt": {
            "type": "number"
          }
        },
        "required": [
          "artifactId",
          "templateSlug",
          "filename",
          "contentType",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "additionalProperties": false
      },
      "CloneWebhookEvent": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "clone.ready",
              "clone.failed"
            ]
          },
          "id": {
            "type": "string",
            "description": "Stable webhook event id. Replayed attempts keep the same event id.",
            "example": "evt_0123456789abcdef0123456789abcdef"
          },
          "mode": {
            "description": "live for real clone events; test for free mock integration events.",
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "cloneRequestId": {
            "type": "string",
            "pattern": "^api_req_[A-Za-z0-9]+$"
          },
          "jobId": {
            "type": "string"
          },
          "templateSlug": {
            "type": "string"
          },
          "previewUrl": {
            "type": "string",
            "format": "uri"
          },
          "error": {
            "type": "string"
          },
          "sourceZip": {
            "$ref": "#/components/schemas/CloneRequestSourceZip"
          },
          "createdAt": {
            "type": "number",
            "description": "Unix epoch timestamp in milliseconds."
          }
        },
        "required": [
          "event",
          "id",
          "mode",
          "cloneRequestId",
          "sourceZip",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "missing_idempotency_key",
                  "invalid_json",
                  "invalid_request",
                  "missing_api_key",
                  "invalid_api_key",
                  "api_key_account_not_found",
                  "insufficient_permissions",
                  "rate_limited",
                  "usage_exceeded",
                  "insufficient_credits",
                  "not_found",
                  "idempotency_conflict",
                  "request_not_ready",
                  "source_artifact_not_ready",
                  "invalid_callback_url",
                  "webhook_not_configured",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      }
    },
    "requestBodies": {
      "CloneWebhookEvent": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CloneWebhookEvent"
            }
          }
        }
      }
    }
  },
  "openapi": "3.1.1",
  "paths": {
    "/api/healthz": {
      "get": {
        "summary": "Health check",
        "description": "Returns the public web application's health status.",
        "operationId": "getHealthz",
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "service"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "UP"
                      ]
                    },
                    "service": {
                      "type": "string",
                      "const": "clonesite-ai"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "summary": "LLM-readable product overview",
        "description": "Returns the concise machine-readable overview of Clonesite AI.",
        "operationId": "getLlmsTxt",
        "responses": {
          "200": {
            "description": "Markdown overview.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/auth.md": {
      "get": {
        "summary": "Agent authentication guidance",
        "description": "Explains the current human-authenticated boundary and future agent-auth direction.",
        "operationId": "getAuthMd",
        "responses": {
          "200": {
            "description": "Markdown authentication guidance.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/clone-requests/preflight": {
      "post": {
        "operationId": "preflightCloneRequest",
        "summary": "Preflight a clone request",
        "description": "Validates a live API key and clone request payload without spending credits, creating an API clone request, queueing generation, or sending webhooks. Use this before the first real live create call.",
        "tags": [
          "Clone API"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCloneRequest"
              },
              "examples": {
                "basic": {
                  "summary": "Preflight a clone request",
                  "value": {
                    "sourceUrl": "https://example.com",
                    "prompt": "Recreate this as a clean SaaS landing page for Acme.",
                    "callbackUrl": "https://client.example.com/webhooks/clonesite"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preflight result. canCreate=false reports account-side blockers such as insufficient credits or missing webhook settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloneRequestPreflight"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "402",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "404",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "409",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/api/v1/clone-requests": {
      "post": {
        "operationId": "createCloneRequest",
        "summary": "Create a clone request",
        "description": "Creates an asynchronous clone request from a public source URL and prompt. The request is accepted immediately, spends 5 credits, and returns HTTP 202 with status=queued plus a statusUrl. Clone generation usually takes about 1-3 hours. Clients should poll GET /api/v1/clone-requests/{id} or wait for a configured webhook. Failed clones are automatically refunded. This endpoint is idempotent per account and Idempotency-Key; reusing the same key with a different payload returns 409.",
        "tags": [
          "Clone API"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCloneRequest"
              },
              "examples": {
                "basic": {
                  "summary": "Create a clone request",
                  "value": {
                    "sourceUrl": "https://example.com",
                    "prompt": "Recreate this as a clean SaaS landing page for Acme.",
                    "externalRequestId": "crm-12345",
                    "callbackUrl": "https://client.example.com/webhooks/clonesite"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Clone request accepted and queued. The returned status is queued; poll statusUrl or wait for the webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloneRequest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "404",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/api/v1/clone-requests/test-runs": {
      "post": {
        "operationId": "createCloneTestRun",
        "summary": "Create a free clone API test run",
        "description": "Creates a free mock clone request for integration testing with a live API key. Test runs do not spend credits, create generation jobs, or use the worker, but they do support polling, signed webhook delivery, and source ZIP fixture download through the same API shapes as live clone requests.",
        "tags": [
          "Clone API"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCloneTestRun"
              },
              "examples": {
                "ready": {
                  "summary": "Create a ready test run",
                  "value": {
                    "sourceUrl": "https://example.com",
                    "prompt": "Recreate this as a clean SaaS landing page for Acme.",
                    "externalRequestId": "crm-test-12345",
                    "callbackUrl": "https://client.example.com/webhooks/clonesite",
                    "scenario": "ready"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Clone API test run accepted. Poll statusUrl or wait for the signed webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloneRequest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "402",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "404",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/api/v1/clone-requests/{id}": {
      "get": {
        "operationId": "getCloneRequest",
        "summary": "Get clone request status",
        "description": "Returns the current asynchronous clone request state. Clone generation usually takes about 1-3 hours after creation. Poll this endpoint until status is ready or failed, or use a callbackUrl webhook when creating the request.",
        "tags": [
          "Clone API"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CloneRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current clone request status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloneRequest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "402",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "409",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "422",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/api/v1/clone-requests/{id}/source-downloads": {
      "post": {
        "operationId": "createCloneRequestSourceDownload",
        "summary": "Unlock and create a source ZIP download URL",
        "description": "Unlocks source ZIP access for a ready clone request and returns a short-lived signed download URL. The first unlock for the template costs 100 credits; later calls for an already-unlocked template return a new signed URL without another unlock charge. Call this only after the clone request status is ready and sourceZip.available is true.",
        "tags": [
          "Clone API"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CloneRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Signed source ZIP download URL. The URL expires after about 5 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceDownload"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "422",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    }
  },
  "webhooks": {
    "clone.ready": {
      "post": {
        "summary": "Clone ready webhook",
        "description": "Sent to callbackUrl when a clone reaches ready. Verify X-Clonesite-Signature before trusting the payload. The signature format is v1=hex(hmac_sha256(secret, X-Clonesite-Timestamp + \".\" + rawBody)). The raw request body bytes must be used exactly as received. The signed timestamp is sent in X-Clonesite-Timestamp as Unix seconds.",
        "operationId": "receiveCloneReadyWebhook",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/CloneWebhookEvent"
        },
        "responses": {
          "200": {
            "description": "Webhook received. Any 2xx status marks delivery as delivered."
          }
        }
      }
    },
    "clone.failed": {
      "post": {
        "summary": "Clone failed webhook",
        "description": "Sent to callbackUrl when a clone reaches failed. Verify X-Clonesite-Signature before trusting the payload. The signature format is v1=hex(hmac_sha256(secret, timestamp + \".\" + rawBody)). The raw request body bytes must be used exactly as received. Failed clone requests are automatically refunded.",
        "operationId": "receiveCloneFailedWebhook",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/CloneWebhookEvent"
        },
        "responses": {
          "200": {
            "description": "Webhook received. Any 2xx status marks delivery as delivered."
          }
        }
      }
    }
  }
}
