{
  "openapi": "3.1.0",
  "info": {
    "title": "Kinoseed API",
    "version": "1.0.0",
    "description": "Generate images and videos with the models on kinoseed.app, billed in credits.\nEvery request is authenticated with an API key created on the account page and sent as a bearer token (or in the X-Kinoseed-Key header).\nPrices are in credits; one credit is priced in `creditUsd` on GET /credits and GET /models.\nA run that the provider refuses is refunded in full.\n\nError codes: unauthorized (401), forbidden_scope (403), insufficient_credits (402), rate_limited (429), invalid_request (400), unknown_model (400), unsafe_url (400), not_found (404), limit_reached (409), idempotency_in_flight (409), idempotency_mismatch (422), too_many_running (429), provider_unavailable (503), internal (500).",
    "contact": {
      "url": "https://kinoseed.app/account"
    },
    "termsOfService": "https://kinoseed.app/terms"
  },
  "servers": [
    {
      "url": "https://kinoseed.app/api/v1"
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "The account behind the key."
    },
    {
      "name": "Catalog",
      "description": "What can be run and what it costs."
    },
    {
      "name": "Generate",
      "description": "Start runs and price them."
    },
    {
      "name": "Jobs",
      "description": "Follow runs to their result."
    },
    {
      "name": "Files",
      "description": "Input files for runs."
    },
    {
      "name": "Webhooks",
      "description": "Be told when a job finishes instead of polling."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key created on the account page, sent as `Authorization: Bearer ks_live_...` (or, for a client that cannot set Authorization, as `X-Kinoseed-Key: ks_live_...` with the same validation; never in the query string). Keys spend the account's credits exactly like the Generate button and are scoped (credits:read, images:generate, videos:generate, jobs:read, jobs:write, files:write, webhooks:manage). Keys do not get webhooks:manage unless it is chosen at creation."
      }
    },
    "schemas": {
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "job.completed",
          "job.failed"
        ],
        "description": "job.completed: a job finished and its outputs are on our storage. job.failed: a job failed and its credits were refunded."
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "completed",
          "failed"
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "progress": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "model": {
            "type": "string"
          },
          "prompt": {
            "type": "string"
          },
          "credits": {
            "type": "object",
            "properties": {
              "charged": {
                "type": "integer"
              },
              "refunded": {
                "type": "integer"
              }
            },
            "required": [
              "charged",
              "refunded"
            ]
          },
          "output": {
            "type": "object",
            "properties": {
              "videoUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "imageUrls": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "thumbUrl": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "videoUrl",
              "imageUrls",
              "thumbUrl"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "In the customer's words; null unless failed."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string",
                "format": "uri"
              },
              "web": {
                "type": "string",
                "format": "uri"
              }
            },
            "required": [
              "self",
              "web"
            ]
          }
        },
        "required": [
          "id",
          "kind",
          "status",
          "progress",
          "model",
          "prompt",
          "credits",
          "output",
          "error",
          "metadata",
          "createdAt",
          "completedAt",
          "links"
        ]
      },
      "WebhookEventPayload": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Event id, `evt_...`; also sent as the Kinoseed-Event-Id header. Use it to drop duplicates."
          },
          "type": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "$ref": "#/components/schemas/Job"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "CreditsResponse": {
        "type": "object",
        "properties": {
          "balance": {
            "type": "integer",
            "description": "Credits the account holds right now."
          },
          "currency": {
            "type": "string",
            "enum": [
              "credits"
            ]
          },
          "creditUsd": {
            "type": "number",
            "description": "What one credit costs at the headline price, in USD."
          },
          "plan": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "example": "starter"
              },
              "status": {
                "type": "string",
                "example": "active"
              },
              "currentPeriodEnd": {
                "type": "string",
                "format": "date-time"
              },
              "cancelAtPeriodEnd": {
                "type": "boolean"
              }
            },
            "required": [
              "id",
              "status",
              "currentPeriodEnd",
              "cancelAtPeriodEnd"
            ],
            "description": "The subscription, or null on an account without a plan."
          }
        },
        "required": [
          "balance",
          "currency",
          "creditUsd",
          "plan"
        ],
        "example": {
          "balance": 1175,
          "currency": "credits",
          "creditUsd": 0.02,
          "plan": null
        }
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "unauthorized",
          "forbidden_scope",
          "insufficient_credits",
          "rate_limited",
          "invalid_request",
          "unknown_model",
          "unsafe_url",
          "not_found",
          "limit_reached",
          "idempotency_in_flight",
          "idempotency_mismatch",
          "too_many_running",
          "provider_unavailable",
          "internal"
        ],
        "description": "Stable, machine-readable error code."
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string",
                "description": "For a person reading a log; wording may change."
              },
              "details": {
                "type": "object",
                "additionalProperties": {},
                "description": "What the client needs to act: `needed`, `balance` and `topUpUrl` on insufficient_credits; `retryAfterSeconds` on rate_limited; `issues` on invalid_request."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ],
        "example": {
          "error": {
            "code": "insufficient_credits",
            "message": "This run costs 25 credits and the account has 0. Top up to continue.",
            "details": {
              "needed": 25,
              "balance": 0,
              "topUpUrl": "https://kinoseed.app/pricing"
            }
          }
        }
      },
      "ModelKind": {
        "type": "string",
        "enum": [
          "image",
          "video",
          "edit"
        ],
        "description": "image: text or reference images to an image. video: text or frames to a clip. edit: a source clip to a new clip."
      },
      "ModelPricingExample": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "credits": {
            "type": "integer"
          },
          "usd": {
            "type": "number",
            "description": "`credits` times `creditUsd`."
          },
          "description": {
            "type": "string",
            "example": "5s at 720p, no sound"
          },
          "estimated": {
            "type": "boolean",
            "description": "True when the provider rate behind this price is an estimate."
          }
        },
        "required": [
          "credits",
          "usd",
          "description",
          "estimated"
        ]
      },
      "Model": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "kling-3"
          },
          "kind": {
            "$ref": "#/components/schemas/ModelKind"
          },
          "name": {
            "type": "string",
            "example": "Kling 3.0"
          },
          "description": {
            "type": "string"
          },
          "inputs": {
            "type": "object",
            "properties": {
              "text": {
                "type": "boolean",
                "description": "Takes a prompt alone."
              },
              "image": {
                "type": "boolean",
                "description": "Takes a start frame (video) or a reference image (image)."
              },
              "endImage": {
                "type": "boolean",
                "description": "Takes an end frame."
              },
              "referenceImages": {
                "type": "integer",
                "description": "How many reference images the multi-reference mode accepts; 0 when there is none."
              },
              "video": {
                "type": "boolean",
                "description": "Takes a source clip."
              }
            },
            "required": [
              "text",
              "image",
              "endImage",
              "referenceImages",
              "video"
            ]
          },
          "aspects": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "16:9",
              "9:16",
              "1:1"
            ]
          },
          "durations": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "description": "Seconds the model can produce; empty for images and edits."
          },
          "resolutions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Empty when the endpoint offers no choice."
          },
          "supportsAudio": {
            "type": "boolean"
          },
          "default": {
            "type": "boolean",
            "description": "Used when a request names no model."
          },
          "pricing": {
            "$ref": "#/components/schemas/ModelPricingExample"
          }
        },
        "required": [
          "id",
          "kind",
          "name",
          "description",
          "inputs",
          "aspects",
          "durations",
          "resolutions",
          "supportsAudio",
          "default",
          "pricing"
        ]
      },
      "ModelsResponse": {
        "type": "object",
        "properties": {
          "creditUsd": {
            "type": "number"
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        },
        "required": [
          "creditUsd",
          "models"
        ]
      },
      "ImageCreateBody": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000
          },
          "model": {
            "type": "string",
            "description": "An image model id from GET /models. Defaults to the catalog default."
          },
          "aspect": {
            "type": "string",
            "example": "1:1"
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "description": "Images per run; the price multiplies."
          },
          "referenceImageUrls": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 2048,
              "format": "uri",
              "description": "Public https URL, or a URL returned by this API."
            },
            "maxItems": 8
          },
          "seed": {
            "type": "integer"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Up to 16 string values the caller wants back on the job."
          }
        },
        "required": [
          "prompt"
        ]
      },
      "VideoCreateBody": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "model": {
            "type": "string",
            "description": "A video model id from GET /models. Defaults to the catalog default."
          },
          "aspect": {
            "type": "string",
            "example": "16:9"
          },
          "duration": {
            "type": "integer",
            "minimum": 2,
            "maximum": 20,
            "description": "Seconds; snapped to the model's nearest allowed length."
          },
          "resolution": {
            "type": "string"
          },
          "audio": {
            "type": "boolean",
            "description": "Generate sound. Off by default; it can double the price."
          },
          "startImageUrl": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "Public https URL, or a URL returned by this API."
          },
          "endImageUrl": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "Public https URL, or a URL returned by this API."
          },
          "referenceImageUrls": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 2048,
              "format": "uri",
              "description": "Public https URL, or a URL returned by this API."
            },
            "maxItems": 9
          },
          "seed": {
            "type": "integer"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Up to 16 string values the caller wants back on the job."
          }
        },
        "required": [
          "prompt"
        ]
      },
      "QuoteResponse": {
        "type": "object",
        "properties": {
          "credits": {
            "type": "integer"
          },
          "usd": {
            "type": "number"
          },
          "endpoint": {
            "type": "string",
            "description": "The provider endpoint the run would land on."
          },
          "estimated": {
            "type": "boolean"
          }
        },
        "required": [
          "credits",
          "usd",
          "endpoint",
          "estimated"
        ]
      },
      "QuoteBody": {
        "oneOf": [
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/ImageCreateBody"
              },
              {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "image"
                    ]
                  }
                },
                "required": [
                  "kind"
                ]
              }
            ]
          },
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/VideoCreateBody"
              },
              {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "video"
                    ]
                  }
                },
                "required": [
                  "kind"
                ]
              }
            ]
          }
        ]
      },
      "JobsListResponse": {
        "type": "object",
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Job"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "jobs",
          "nextCursor"
        ]
      },
      "JobDeleteResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "FileUploadResponse": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "size": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "example": "image/png"
          }
        },
        "required": [
          "url",
          "size",
          "type"
        ]
      },
      "FileUploadBody": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "description": "An image (png, jpeg, webp, gif), audio or video file, up to 200 MB.",
            "format": "binary"
          }
        },
        "required": [
          "file"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "active": {
            "type": "boolean",
            "description": "False once 20 delivery attempts in a row have failed. A successful POST /webhooks/{id}/test switches it back on."
          },
          "failureCount": {
            "type": "integer",
            "description": "Failed delivery attempts since the last success."
          },
          "disabledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastDeliveredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "active",
          "failureCount",
          "disabledAt",
          "lastDeliveredAt",
          "createdAt"
        ]
      },
      "WebhooksListResponse": {
        "type": "object",
        "properties": {
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          }
        },
        "required": [
          "webhooks"
        ]
      },
      "WebhookCreatedResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "The signing secret, `whsec_` and 43 characters. Shown only in this response: store it now.",
                "example": "whsec_..."
              }
            },
            "required": [
              "secret"
            ]
          }
        ]
      },
      "WebhookCreateBody": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "A public https URL that accepts POST. Private addresses, IP literals and plain http are refused.",
            "example": "https://example.com/hooks/kinoseed"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "minItems": 1,
            "description": "The events to send. Defaults to both."
          }
        },
        "required": [
          "url"
        ]
      },
      "WebhookDeleteResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "WebhookTestResponse": {
        "type": "object",
        "properties": {
          "delivered": {
            "type": "boolean",
            "description": "Whether the endpoint answered with a 2xx status."
          },
          "status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The endpoint's HTTP status; null when it could not be reached."
          },
          "latencyMs": {
            "type": "integer"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "eventId": {
            "type": "string",
            "description": "The `evt_` id of the ping that was sent."
          }
        },
        "required": [
          "delivered",
          "status",
          "latencyMs",
          "error",
          "eventId"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/credits": {
      "get": {
        "operationId": "getCredits",
        "summary": "Credit balance",
        "description": "The account's credit balance and subscription. Needs the `credits:read` scope.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Balance and plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks credits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks credits:read."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over the key's request budget; see Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over the key's request budget; see Retry-After."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl https://kinoseed.app/api/v1/credits \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/credits\", {\n  headers: { Authorization: `Bearer ${KEY}` },\n});\nconst { balance, creditUsd, plan } = await res.json();\nconsole.log(balance, \"credits\", plan?.id ?? \"no plan\");\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.get(\"https://kinoseed.app/api/v1/credits\", headers={\"Authorization\": f\"Bearer {KEY}\"})\nr.raise_for_status()\ndata = r.json()\nprint(data[\"balance\"], \"credits\", data[\"plan\"])\n"
          }
        ]
      }
    },
    "/models": {
      "get": {
        "operationId": "listModels",
        "summary": "Model catalog",
        "description": "Every model the API can run, with the inputs it takes, its aspects, durations and resolutions, and an example price in credits. Public; no key needed. Cached for five minutes.",
        "tags": [
          "Catalog"
        ],
        "parameters": [
          {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ModelKind"
                },
                {
                  "description": "Only models of this kind."
                }
              ]
            },
            "required": false,
            "name": "kind",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Unknown `kind`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Unknown `kind`."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl \"https://kinoseed.app/api/v1/models?kind=video\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const res = await fetch(\"https://kinoseed.app/api/v1/models?kind=video\");\nconst { creditUsd, models } = await res.json();\nfor (const m of models) {\n  console.log(m.id, m.pricing?.credits, \"credits for\", m.pricing?.description);\n}\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import httpx\n\nr = httpx.get(\"https://kinoseed.app/api/v1/models\", params={\"kind\": \"video\"})\nr.raise_for_status()\nfor m in r.json()[\"models\"]:\n    print(m[\"id\"], m[\"pricing\"] and m[\"pricing\"][\"credits\"])\n"
          }
        ]
      }
    },
    "/images": {
      "post": {
        "operationId": "createImage",
        "summary": "Generate images",
        "description": "Start an image run. The price is quoted from the model and `count`, charged before the provider is called, and refunded in full if the provider refuses. Reference images must be public https URLs or URLs returned by POST /files. Needs `images:generate`.",
        "tags": [
          "Generate"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "maxLength": 255,
              "description": "Any string of your choosing, up to 255 characters. A retry with the same key and body returns the first response (with `Idempotent-Replayed: true`) instead of starting and charging a second run. Kept for 24 hours."
            },
            "required": false,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImageCreateBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The provider refused the request at submission. The Job has status failed, `error` says why, and `credits.refunded` equals `credits.charged`: nothing was spent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "201": {
            "description": "The image run was started and charged. Poll GET /jobs/{id} (or use ?wait=) until status is completed or failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Body failed validation (`invalid_request`), the model is not in the catalog (`unknown_model`), or a URL is not public https (`unsafe_url`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Body failed validation (`invalid_request`), the model is not in the catalog (`unknown_model`), or a URL is not public https (`unsafe_url`)."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "402": {
            "description": "The balance does not cover the quote; `details` has `needed`, `balance` and `topUpUrl`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "insufficient_credits",
                    "message": "The balance does not cover the quote; `details` has `needed`, `balance` and `topUpUrl`."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the images:generate scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks the images:generate scope."
                  }
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still being processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_flight",
                    "message": "A request with this Idempotency-Key is still being processed."
                  }
                }
              }
            }
          },
          "422": {
            "description": "This Idempotency-Key was already used with a different body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_mismatch",
                    "message": "This Idempotency-Key was already used with a different body."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X POST https://kinoseed.app/api/v1/images \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: order-1042-hero\" \\\n  -d '{\n    \"prompt\": \"a ceramic mug on a sunlit desk, product photo\",\n    \"model\": \"nano-banana-pro\",\n    \"aspect\": \"1:1\",\n    \"count\": 2\n  }'\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/images\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${KEY}`,\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"order-1042-hero\",\n  },\n  body: JSON.stringify({\n    prompt: \"a ceramic mug on a sunlit desk, product photo\",\n    model: \"nano-banana-pro\",\n    aspect: \"1:1\",\n    count: 2,\n  }),\n});\nif (!res.ok) throw new Error((await res.json()).error.code);\nconst job = await res.json(); // { id, status: \"queued\", credits: { charged }, ... }\nconsole.log(job.id, job.credits.charged, \"credits\");\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.post(\n    \"https://kinoseed.app/api/v1/images\",\n    headers={\"Authorization\": f\"Bearer {KEY}\", \"Idempotency-Key\": \"order-1042-hero\"},\n    json={\n        \"prompt\": \"a ceramic mug on a sunlit desk, product photo\",\n        \"model\": \"nano-banana-pro\",\n        \"aspect\": \"1:1\",\n        \"count\": 2,\n    },\n)\nr.raise_for_status()\njob = r.json()\nprint(job[\"id\"], job[\"credits\"][\"charged\"], \"credits\")\n"
          }
        ]
      }
    },
    "/videos": {
      "post": {
        "operationId": "createVideo",
        "summary": "Generate a video",
        "description": "Start a video run. The price depends on the model, duration, resolution, sound and whether frames or reference images are sent; it is charged before the provider is called and refunded in full if the provider refuses. Needs `videos:generate`.",
        "tags": [
          "Generate"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "maxLength": 255,
              "description": "Any string of your choosing, up to 255 characters. A retry with the same key and body returns the first response (with `Idempotent-Replayed: true`) instead of starting and charging a second run. Kept for 24 hours."
            },
            "required": false,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoCreateBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The provider refused the request at submission. The Job has status failed, `error` says why, and `credits.refunded` equals `credits.charged`: nothing was spent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "201": {
            "description": "The video run was started and charged. Poll GET /jobs/{id} (or use ?wait=) until status is completed or failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Body failed validation (`invalid_request`), the model is not in the catalog (`unknown_model`), or a URL is not public https (`unsafe_url`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Body failed validation (`invalid_request`), the model is not in the catalog (`unknown_model`), or a URL is not public https (`unsafe_url`)."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "402": {
            "description": "The balance does not cover the quote; `details` has `needed`, `balance` and `topUpUrl`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "insufficient_credits",
                    "message": "The balance does not cover the quote; `details` has `needed`, `balance` and `topUpUrl`."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the videos:generate scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks the videos:generate scope."
                  }
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still being processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_flight",
                    "message": "A request with this Idempotency-Key is still being processed."
                  }
                }
              }
            }
          },
          "422": {
            "description": "This Idempotency-Key was already used with a different body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_mismatch",
                    "message": "This Idempotency-Key was already used with a different body."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X POST https://kinoseed.app/api/v1/videos \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"prompt\": \"slow push in on the mug as steam rises, morning light\",\n    \"model\": \"kling-3\",\n    \"duration\": 5,\n    \"aspect\": \"16:9\",\n    \"startImageUrl\": \"https://example.com/mug.png\"\n  }'\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/videos\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    prompt: \"slow push in on the mug as steam rises, morning light\",\n    model: \"kling-3\",\n    duration: 5,\n    aspect: \"16:9\",\n    startImageUrl: \"https://example.com/mug.png\",\n  }),\n});\nif (!res.ok) throw new Error((await res.json()).error.code);\nconst job = await res.json();\nconsole.log(job.id, job.status); // poll GET /jobs/{id}?wait=50 until completed\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.post(\n    \"https://kinoseed.app/api/v1/videos\",\n    headers={\"Authorization\": f\"Bearer {KEY}\"},\n    json={\n        \"prompt\": \"slow push in on the mug as steam rises, morning light\",\n        \"model\": \"kling-3\",\n        \"duration\": 5,\n        \"aspect\": \"16:9\",\n        \"startImageUrl\": \"https://example.com/mug.png\",\n    },\n)\nr.raise_for_status()\njob = r.json()\nprint(job[\"id\"], job[\"status\"])  # poll GET /jobs/{id}?wait=50 until completed\n"
          }
        ]
      }
    },
    "/quote": {
      "post": {
        "operationId": "quote",
        "summary": "Price a run",
        "description": "What POST /images or POST /videos would charge for this body, without starting anything. Send the same body with a `kind` field. Any live key.",
        "tags": [
          "Generate"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The price in credits and USD.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Body failed validation, or the model is not in the catalog (`unknown_model`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Body failed validation, or the model is not in the catalog (`unknown_model`)."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X POST https://kinoseed.app/api/v1/quote \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"kind\": \"video\", \"prompt\": \"a lighthouse at dusk\", \"model\": \"kling-3\", \"duration\": 10, \"audio\": true }'\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/quote\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    kind: \"video\",\n    prompt: \"a lighthouse at dusk\",\n    model: \"kling-3\",\n    duration: 10,\n    audio: true,\n  }),\n});\nconst { credits, usd, estimated } = await res.json();\nconsole.log(`${credits} credits (about $${usd})${estimated ? \", estimated\" : \"\"}`);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.post(\n    \"https://kinoseed.app/api/v1/quote\",\n    headers={\"Authorization\": f\"Bearer {KEY}\"},\n    json={\"kind\": \"video\", \"prompt\": \"a lighthouse at dusk\", \"model\": \"kling-3\", \"duration\": 10, \"audio\": True},\n)\nr.raise_for_status()\nq = r.json()\nprint(q[\"credits\"], \"credits, about $\", q[\"usd\"])\n"
          }
        ]
      }
    },
    "/jobs": {
      "get": {
        "operationId": "listJobs",
        "summary": "List jobs",
        "description": "The account's jobs, newest first, including runs started in the web app. Page with `cursor` from `nextCursor`. Needs `jobs:read`.",
        "tags": [
          "Jobs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/JobStatus"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            },
            "required": false,
            "name": "kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobsListResponse"
                }
              }
            }
          },
          "400": {
            "description": "A query parameter is out of range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A query parameter is out of range."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks jobs:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks jobs:read."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl \"https://kinoseed.app/api/v1/jobs?limit=20&status=completed&kind=video\" \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/jobs?limit=20&status=completed&kind=video\", {\n  headers: { Authorization: `Bearer ${KEY}` },\n});\nconst { jobs, nextCursor } = await res.json();\nconsole.log(jobs.length, \"jobs; next page:\", nextCursor);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.get(\"https://kinoseed.app/api/v1/jobs\", params={\"limit\": 20, \"status\": \"completed\", \"kind\": \"video\"},\n              headers={\"Authorization\": f\"Bearer {KEY}\"})\nr.raise_for_status()\npage = r.json()\nprint(len(page[\"jobs\"]), \"jobs; next page:\", page[\"nextCursor\"])\n"
          }
        ]
      }
    },
    "/jobs/{id}": {
      "get": {
        "operationId": "getJob",
        "summary": "Read a job",
        "description": "One job, advanced against the provider on each read. With `?wait=N` (1 to 50 seconds) the response is held until the job finishes or the time is up, polling every 3 seconds. Needs `jobs:read`.",
        "tags": [
          "Jobs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The job id returned when it was created."
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "description": "Hold the response up to this many seconds while the job is still running."
            },
            "required": false,
            "name": "wait",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "`wait` is out of range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "`wait` is out of range."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks jobs:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks jobs:read."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No job with this id belongs to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "No job with this id belongs to this account."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "# Hold the response up to 50 seconds while the job runs.\ncurl \"https://kinoseed.app/api/v1/jobs/JOB_ID?wait=50\" \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nasync function waitForJob(id: string) {\n  for (;;) {\n    const res = await fetch(`https://kinoseed.app/api/v1/jobs/${id}?wait=50`, {\n      headers: { Authorization: `Bearer ${KEY}` },\n    });\n    const job = await res.json();\n    if (job.status === \"completed\" || job.status === \"failed\") return job;\n  }\n}\n\nconst job = await waitForJob(\"JOB_ID\");\nconsole.log(job.status, job.output.imageUrls, job.output.videoUrl, job.error);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\ndef wait_for_job(job_id: str) -> dict:\n    with httpx.Client(timeout=60) as client:\n        while True:\n            r = client.get(f\"https://kinoseed.app/api/v1/jobs/{job_id}\", params={\"wait\": 50},\n                           headers={\"Authorization\": f\"Bearer {KEY}\"})\n            r.raise_for_status()\n            job = r.json()\n            if job[\"status\"] in (\"completed\", \"failed\"):\n                return job\n\njob = wait_for_job(\"JOB_ID\")\nprint(job[\"status\"], job[\"output\"], job[\"error\"])\n"
          }
        ]
      },
      "delete": {
        "operationId": "deleteJob",
        "summary": "Delete a job",
        "description": "Remove a job from the account's library. Credits are not affected: a refused run was already refunded, a finished one was delivered. Needs `jobs:write`.",
        "tags": [
          "Jobs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The job id returned when it was created."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobDeleteResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks jobs:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks jobs:write."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No job with this id belongs to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "No job with this id belongs to this account."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X DELETE https://kinoseed.app/api/v1/jobs/JOB_ID \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/jobs/JOB_ID\", {\n  method: \"DELETE\",\n  headers: { Authorization: `Bearer ${KEY}` },\n});\nconsole.log(await res.json()); // { id, deleted: true }\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.delete(\"https://kinoseed.app/api/v1/jobs/JOB_ID\", headers={\"Authorization\": f\"Bearer {KEY}\"})\nr.raise_for_status()\nprint(r.json())  # {\"id\": ..., \"deleted\": True}\n"
          }
        ]
      }
    },
    "/files": {
      "post": {
        "operationId": "uploadFile",
        "summary": "Upload an input file",
        "description": "Store an image, audio or video file (up to 200 MB) and get back a URL that POST /images and POST /videos accept as a reference or frame. Needs `files:write`.",
        "tags": [
          "Files"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/FileUploadBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileUploadResponse"
                }
              }
            }
          },
          "400": {
            "description": "Not multipart, no `file` field, too large (413) or an unsupported type (415).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Not multipart, no `file` field, too large (413) or an unsupported type (415)."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks files:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks files:write."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          },
          "503": {
            "description": "Storage is not available right now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "provider_unavailable",
                    "message": "Storage is not available right now."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X POST https://kinoseed.app/api/v1/files \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\" \\\n  -F \"file=@./mug.png\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\nimport { openAsBlob } from \"node:fs\";\n\nconst form = new FormData();\nform.set(\"file\", await openAsBlob(\"./mug.png\", { type: \"image/png\" }), \"mug.png\");\nconst res = await fetch(\"https://kinoseed.app/api/v1/files\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${KEY}` },\n  body: form,\n});\nconst { url } = await res.json(); // pass as startImageUrl or referenceImageUrls\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nwith open(\"mug.png\", \"rb\") as f:\n    r = httpx.post(\"https://kinoseed.app/api/v1/files\", headers={\"Authorization\": f\"Bearer {KEY}\"},\n                   files={\"file\": (\"mug.png\", f, \"image/png\")})\nr.raise_for_status()\nurl = r.json()[\"url\"]  # pass as startImageUrl or referenceImageUrls\n"
          }
        ]
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhook endpoints",
        "description": "The account's webhook endpoints, oldest first. Secrets are never returned here. Needs `webhooks:manage`.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhooksListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation)."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl https://kinoseed.app/api/v1/webhooks \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/webhooks\", {\n  headers: { Authorization: `Bearer ${KEY}` },\n});\nconst { webhooks } = await res.json();\nfor (const w of webhooks) console.log(w.id, w.url, w.active, w.events);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.get(\"https://kinoseed.app/api/v1/webhooks\", headers={\"Authorization\": f\"Bearer {KEY}\"})\nr.raise_for_status()\nfor w in r.json()[\"webhooks\"]:\n    print(w[\"id\"], w[\"url\"], w[\"active\"], w[\"events\"])\n"
          }
        ]
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Create a webhook endpoint",
        "description": "Register a public https URL to receive job.completed and job.failed events, signed with the returned secret. The secret is in this response only. Up to 5 endpoints per account. Needs `webhooks:manage`.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. Store `secret` now; it is never shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Body failed validation (`invalid_request`), or the URL is not public https (`unsafe_url`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Body failed validation (`invalid_request`), or the URL is not public https (`unsafe_url`)."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation)."
                  }
                }
              }
            }
          },
          "409": {
            "description": "The account already has 5 endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "limit_reached",
                    "message": "The account already has 5 endpoints."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X POST https://kinoseed.app/api/v1/webhooks \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"url\": \"https://example.com/hooks/kinoseed\", \"events\": [\"job.completed\", \"job.failed\"] }'\n# The response holds \"secret\": \"whsec_...\" once. Store it now.\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/webhooks\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({ url: \"https://example.com/hooks/kinoseed\" }),\n});\nif (!res.ok) throw new Error((await res.json()).error.code);\nconst { id, secret } = await res.json();\n// secret is shown only in this response: put it in your secret store now.\nconsole.log(id);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.post(\"https://kinoseed.app/api/v1/webhooks\", headers={\"Authorization\": f\"Bearer {KEY}\"},\n               json={\"url\": \"https://example.com/hooks/kinoseed\"})\nr.raise_for_status()\nhook = r.json()\n# hook[\"secret\"] is shown only in this response: put it in your secret store now.\nprint(hook[\"id\"])\n"
          }
        ]
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook endpoint",
        "description": "Remove an endpoint. Deliveries still waiting for a retry are dropped with it. Needs `webhooks:manage`.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The webhook endpoint id."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeleteResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation)."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No webhook endpoint with this id belongs to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "No webhook endpoint with this id belongs to this account."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X DELETE https://kinoseed.app/api/v1/webhooks/WEBHOOK_ID \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/webhooks/WEBHOOK_ID\", {\n  method: \"DELETE\",\n  headers: { Authorization: `Bearer ${KEY}` },\n});\nconsole.log(await res.json()); // { id, deleted: true }\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.delete(\"https://kinoseed.app/api/v1/webhooks/WEBHOOK_ID\", headers={\"Authorization\": f\"Bearer {KEY}\"})\nr.raise_for_status()\nprint(r.json())  # {\"id\": ..., \"deleted\": True}\n"
          }
        ]
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Send a test event",
        "description": "Send a signed `ping` event to the endpoint now and answer with its HTTP status and latency. A 2xx answer switches a disabled endpoint back on and clears its failure count. Limited to 10 tests an hour per endpoint and 30 per account. `error` is one of: connection refused, timed out, DNS lookup failed, TLS error, blocked address, network error, or `HTTP <status>`. Needs `webhooks:manage`.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The webhook endpoint id."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "What the endpoint answered. `delivered` is false for a non-2xx status, a timeout or an unreachable host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing, invalid or revoked key."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "The key lacks webhooks:manage (keys do not get it unless it is ticked at creation)."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No webhook endpoint with this id belongs to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "No webhook endpoint with this id belongs to this account."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Over a rate budget; see Retry-After. Also `too_many_running` when the account already has 5 runs in progress."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl -X POST https://kinoseed.app/api/v1/webhooks/WEBHOOK_ID/test \\\n  -H \"Authorization: Bearer ks_live_YOUR_KEY\"\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const KEY = process.env.KINOSEED_API_KEY!; // ks_live_YOUR_KEY\n\nconst res = await fetch(\"https://kinoseed.app/api/v1/webhooks/WEBHOOK_ID/test\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${KEY}` },\n});\nconst { delivered, status, latencyMs, error } = await res.json();\nconsole.log(delivered ? `ok, HTTP ${status} in ${latencyMs} ms` : `failed: ${error}`);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os, httpx\n\nKEY = os.environ[\"KINOSEED_API_KEY\"]  # ks_live_YOUR_KEY\n\nr = httpx.post(\"https://kinoseed.app/api/v1/webhooks/WEBHOOK_ID/test\", headers={\"Authorization\": f\"Bearer {KEY}\"})\nr.raise_for_status()\nt = r.json()\nprint(\"ok\" if t[\"delivered\"] else \"failed\", t[\"status\"], t[\"latencyMs\"], t[\"error\"])\n"
          }
        ]
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "summary": "This document",
        "description": "The OpenAPI 3.1 description of this API. Also served at the site root as /openapi.json.",
        "tags": [
          "Catalog"
        ],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl https://kinoseed.app/openapi.json\n"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript",
            "source": "const doc = await (await fetch(\"https://kinoseed.app/openapi.json\")).json();\nconsole.log(doc.info.version, Object.keys(doc.paths));\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import httpx\n\ndoc = httpx.get(\"https://kinoseed.app/openapi.json\").json()\nprint(doc[\"info\"][\"version\"], list(doc[\"paths\"]))\n"
          }
        ]
      }
    }
  },
  "webhooks": {
    "job.completed": {
      "post": {
        "summary": "A job finished; its outputs are on our storage.",
        "description": "Sent to every active endpoint subscribed to job.completed. Answer with any 2xx within 5 seconds. Failed attempts are retried after 1 minute, 5 minutes, 30 minutes and 2 hours; after 20 failed attempts in a row the endpoint is switched off.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of t + \".\" + raw body, keyed with the whole whsec_ secret>`."
            },
            "required": true,
            "name": "Kinoseed-Signature",
            "in": "header"
          },
          {
            "schema": {
              "type": "string",
              "description": "The event id, the same as `id` in the body."
            },
            "required": true,
            "name": "Kinoseed-Event-Id",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status acknowledges the event."
          }
        }
      }
    },
    "job.failed": {
      "post": {
        "summary": "A job failed; its credits were refunded.",
        "description": "Sent to every active endpoint subscribed to job.failed. Answer with any 2xx within 5 seconds. Failed attempts are retried after 1 minute, 5 minutes, 30 minutes and 2 hours; after 20 failed attempts in a row the endpoint is switched off.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of t + \".\" + raw body, keyed with the whole whsec_ secret>`."
            },
            "required": true,
            "name": "Kinoseed-Signature",
            "in": "header"
          },
          {
            "schema": {
              "type": "string",
              "description": "The event id, the same as `id` in the body."
            },
            "required": true,
            "name": "Kinoseed-Event-Id",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status acknowledges the event."
          }
        }
      }
    }
  }
}