{
  "openapi": "3.1.0",
  "info": {
    "title": "Seedancee API",
    "version": "1.1.0",
    "summary": "Create Seedance video generations, poll jobs and receive signed webhooks.",
    "description": "REST API over the same generation service, credits and refund rules as the Seedancee workspace. API access is included in the Pro plan; the API is in early access and must also be switched on for the platform (developer_api feature flag). While it is off every call returns 403 api_disabled; workspaces without an active Pro subscription (and not admitted to early access) get 403 plan_required. Keys are created in the dashboard under Developer API (up to 10 active keys per workspace). All requests and responses are JSON with snake_case fields. Every response carries an X-Request-Id header. Seedancee is an independent service and is not affiliated with ByteDance.",
    "contact": {
      "name": "Seedancee support",
      "url": "https://seedancee.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://seedancee.com/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Models"
    },
    {
      "name": "Uploads"
    },
    {
      "name": "Generations"
    },
    {
      "name": "Credits"
    },
    {
      "name": "Webhooks"
    }
  ],
  "paths": {
    "/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "listModels",
        "summary": "List available models",
        "description": "Active and degraded models with capabilities and retail credit pricing. Requires the models:read scope.",
        "responses": {
          "200": {
            "description": "Model list",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "default_model"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Model"
                      }
                    },
                    "default_model": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/uploads": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "createUpload",
        "summary": "Create an upload (presigned PUT URL)",
        "description": "Registers a reference image, video or audio file and returns a presigned URL valid for 15 minutes. PUT the file to upload_url with exactly upload_headers, then call POST /uploads/{id}/complete. Use the returned id in generation references. Requires uploads:write.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "type",
                  "mime",
                  "bytes",
                  "filename"
                ],
                "properties": {
                  "type": {
                    "enum": [
                      "image",
                      "video",
                      "audio"
                    ]
                  },
                  "mime": {
                    "type": "string",
                    "examples": [
                      "image/png"
                    ]
                  },
                  "bytes": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "filename": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "model": {
                    "type": "string",
                    "description": "Validate size and format against this model's input limits."
                  }
                }
              },
              "example": {
                "type": "image",
                "mime": "image/png",
                "bytes": 1048576,
                "filename": "first-frame.png",
                "model": "seedance-2.5"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PendingUpload"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/uploads/{id}": {
      "get": {
        "tags": [
          "Uploads"
        ],
        "operationId": "getUpload",
        "summary": "Retrieve an upload",
        "description": "Status of an upload of the key's workspace, for polling after /complete while it is validating (every few seconds; each poll counts toward the key's rate limit). ready: usable as a reference. failed: error says why; upload the file again. Requires uploads:write.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "The upload",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upload"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/uploads/{id}/complete": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "completeUpload",
        "summary": "Mark an upload as complete",
        "description": "Verifies the object exists in storage, checks its real size against the storage quota and that its bytes match the declared type. Images whose header can be read are ready at once; videos, audio and other images return status validating while the file is probed: poll GET /uploads/{id} until the status is ready or failed. A rejected upload repeats its reason on every later call. Requires uploads:write.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "width": {
                    "type": "integer"
                  },
                  "height": {
                    "type": "integer"
                  },
                  "duration": {
                    "type": "number",
                    "description": "Seconds (video/audio)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload ready, or validating (poll GET /uploads/{id})",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upload"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "upload_missing (the PUT has not landed yet) or asset_not_pending"
          },
          "413": {
            "$ref": "#/components/responses/Error",
            "description": "too_large (above the size limit) or storage_quota_exceeded (the workspace storage is full; delete unused files). The upload is removed."
          },
          "422": {
            "$ref": "#/components/responses/ValidationError",
            "description": "content_mismatch (the bytes are not the declared type), mime_mismatch, empty_file, or a media-limit code (for example resolution or duration out of range). The upload is removed."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/videos/generations": {
      "post": {
        "tags": [
          "Generations"
        ],
        "operationId": "createGeneration",
        "summary": "Create a video generation",
        "description": "Validates the request against the model's capabilities and the workspace's plan, reserves credits and submits the job. Plan rules are the same as in the dashboard: Pro includes every model, resolution (4K on Seedance 2.0), duration up to the model's maximum, reference videos, edit and extend; an early-access workspace on Starter is limited to Seedance 2.0 Mini, 2.0 Fast and 1.5 Pro, 720p and 10 seconds (duration auto counts as the model's maximum) without reference videos, edit or extend (403 plan_required), and a workspace without a subscription cannot generate (402 subscription_required). Nothing is reserved for invalid or refused requests; failed jobs release the reservation automatically. Limited to 10 creates per minute per user, counted together with generations started in the dashboard (429 with limit_scope user_generations). Requires generations:write. If the provider does not confirm the submission (timeout or dropped connection), the answer is still 202 but with status failed, credits_reserved 0 and error.code submit_unconfirmed; replaying the same Idempotency-Key returns the same answer. No generation.failed webhook is sent for that case, because the response already carries it. A generation that failed with error code submit_unconfirmed (the provider did not confirm the submission) or timeout (it did not finish in time) had its reservation released, but the provider may still deliver it (its late callback is accepted, and timed-out jobs are polled for 24 hours). If the video arrives, the generation becomes succeeded, credits_captured is charged then, and a generation.succeeded webhook is sent. Do not resubmit such a job right away, or it may render (and be charged) twice.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "1-255 printable ASCII characters, scoped to the API key. Replaying the same key with the same body returns the original generation (Idempotent-Replayed: true) without a second charge; a different body returns 409.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGenerationRequest"
              },
              "example": {
                "model": "seedance-2.5",
                "mode": "text-to-video",
                "prompt": "A cinematic product reveal of a matte black earbud case on slate, slow orbit, warm rim light.",
                "duration": 8,
                "aspect_ratio": "16:9",
                "resolution": "720p",
                "generate_audio": true,
                "webhook_url": "https://example.com/hooks/seedancee",
                "metadata": {
                  "order_id": "1234"
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; poll the generation or wait for a webhook. status failed with error.code submit_unconfirmed means the submission could not be confirmed (see the description).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "description": "Present (\"true\") when the response replays an earlier request.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/GenerationPaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/GenerationForbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "get": {
        "tags": [
          "Generations"
        ],
        "operationId": "listGenerations",
        "summary": "List generations",
        "description": "Newest first, all generations of the key's workspace. Requires generations:read.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "description": "Generation id to paginate after (the last id of the previous page).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of generations",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Generation"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/videos/generations/{id}": {
      "get": {
        "tags": [
          "Generations"
        ],
        "operationId": "getGeneration",
        "summary": "Retrieve a generation",
        "description": "Status, outputs (signed URLs valid for 1 hour; fetch again for a fresh URL) and credit settlement. Only generations of the key's workspace are visible. Requires generations:read.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "The generation",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/videos/generations/{id}/cancel": {
      "post": {
        "tags": [
          "Generations"
        ],
        "operationId": "cancelGeneration",
        "summary": "Cancel a queued generation",
        "description": "Cancels a job that has not reached the provider yet and releases the reserved credits. A job already sent to the provider keeps rendering and is charged when it finishes (409 not_cancellable). Requires generations:write.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled generation",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/credits": {
      "get": {
        "tags": [
          "Credits"
        ],
        "operationId": "getCredits",
        "summary": "Credit balance",
        "description": "balance is spendable now (open reservations already deducted); reserved is held by running jobs. Requires credits:read.",
        "responses": {
          "200": {
            "description": "Balance",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "balance",
                    "reserved",
                    "currency_units"
                  ],
                  "properties": {
                    "object": {
                      "const": "credit_balance"
                    },
                    "balance": {
                      "type": "integer"
                    },
                    "reserved": {
                      "type": "integer"
                    },
                    "currency_units": {
                      "const": "credits"
                    }
                  }
                },
                "example": {
                  "object": "credit_balance",
                  "balance": 4120,
                  "reserved": 753,
                  "currency_units": "credits"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "testWebhook",
        "summary": "Send a signed test event",
        "description": "Sends a signed ping event to every active endpoint of the workspace, one endpoint (endpoint_id) or an https URL (url, signed with the per-request signing secret). Returns one result per destination; test pings are not retried. Requires generations:write.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Delivery results",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TestDeliveryResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "webhooks": {
    "generation.succeeded": {
      "post": {
        "summary": "A generation finished and credits were captured",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerationEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledge within 10 seconds. Any other status (including redirects) is retried after 1m, 5m, 30m, 2h and 6h."
          }
        },
        "description": "Sent when the video is ready and charged, including for a generation that had failed with submit_unconfirmed or timeout and was delivered late."
      }
    },
    "generation.failed": {
      "post": {
        "summary": "A generation failed and the reservation was released",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerationEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged"
          }
        },
        "description": "A failure with error code timeout or submit_unconfirmed can still be followed by generation.succeeded if the provider delivers the video later (it is charged then). When the create request itself could not confirm the submission, the create response reports submit_unconfirmed and this webhook is not sent."
      }
    },
    "generation.cancelled": {
      "post": {
        "summary": "A generation was cancelled and the reservation was released",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerationEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged"
          }
        }
      }
    },
    "ping": {
      "post": {
        "summary": "Test event from POST /webhooks/test or the dashboard",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PingEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sdk_live_<43 base64url chars>",
        "description": "API keys are created in the dashboard (Developer API) and shown once; up to 10 active keys per workspace, and a revoked key cannot be restored. API access is included in the Pro plan. Scopes: models:read, generations:read, generations:write, uploads:write, credits:read."
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "SignatureHeader": {
        "name": "Seedancee-Signature",
        "in": "header",
        "required": true,
        "description": "t=<unix seconds>,v1=<hex HMAC-SHA256(signing secret, t + \".\" + raw body)>. Reject timestamps older than 5 minutes.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "Unique id of this request; include it when contacting support.",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests allowed per minute for this key (20 by default, never more than 120).",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix time (seconds) when the window has fully reset.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed JSON or query parameter",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid, revoked or expired API key (codes: missing_api_key, invalid_api_key, api_key_revoked, api_key_expired)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "api_disabled (the API is not switched on yet), plan_required (the workspace has no active Pro subscription and is not in early access; includes required_plan, required_tier and tier), insufficient_scope, insufficient_role or key_owner_removed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "permission_error",
                "code": "plan_required",
                "message": "Developer API access is included in the Pro plan. Upgrade this workspace to Pro to use its API keys.",
                "required_plan": "pro"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Unknown id or not in this workspace",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "not_cancellable, idempotency_key_reused, upload_missing",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "The balance is lower than the quoted reservation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "insufficient_credits_error",
                "code": "insufficient_credits",
                "message": "Insufficient credits: required 753, balance 120.",
                "required": 753,
                "balance": 120
              }
            }
          }
        }
      },
      "GenerationPaymentRequired": {
        "description": "insufficient_credits (type insufficient_credits_error: the balance is lower than the quoted reservation; includes required and balance) or subscription_required (type permission_error: the workspace has no active subscription; includes field, required_tier, required_plan and tier). subscription_required: nothing is stored or reserved. insufficient_credits: the generation is recorded as failed and no credits are reserved; replaying the same Idempotency-Key returns the same 402.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "insufficient_credits": {
                "value": {
                  "error": {
                    "type": "insufficient_credits_error",
                    "code": "insufficient_credits",
                    "message": "Insufficient credits: required 753, balance 120.",
                    "required": 753,
                    "balance": 120
                  }
                }
              },
              "subscription_required": {
                "value": {
                  "error": {
                    "type": "permission_error",
                    "code": "subscription_required",
                    "message": "Subscribe to a plan to generate videos.",
                    "field": "subscription",
                    "required_tier": "starter",
                    "required_plan": "starter",
                    "tier": "free"
                  }
                }
              }
            }
          }
        }
      },
      "GenerationForbidden": {
        "description": "The key-level 403 codes (api_disabled, plan_required for API access, insufficient_scope, insufficient_role, key_owner_removed), or plan_required for an option the workspace's plan does not include: param names the request parameter and field one of model, mode, referenceVideo, resolution, duration (duration auto counts as the model's maximum). Includes required_tier, required_plan and tier. Nothing is stored or reserved.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "permission_error",
                "code": "plan_required",
                "message": "Your plan allows up to 720p. Higher resolutions are available on the Pro plan.",
                "param": "resolution",
                "field": "resolution",
                "required_tier": "pro",
                "required_plan": "pro",
                "tier": "starter"
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "The request is well-formed but not valid for the model (param names the offending field)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "invalid_request_error",
                "code": "resolution_unsupported",
                "message": "Seedance 2.5 supports 480p, 720p, 1080p.",
                "param": "resolution"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded: the key's per-minute limit (limit_scope api_key) or, on generation creates, the per-user limit of 10 per minute (limit_scope user_generations). Wait Retry-After seconds.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProviderError": {
        "description": "The upstream provider rejected the job; the reservation was released",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "generation_unavailable or storage_unavailable: the deployment is not configured for this feature (nothing was charged). tier_unavailable (any authenticated endpoint): the workspace's plan could not be read, so access and plan entitlements cannot be checked; nothing was stored or charged, retryable is true and a Retry-After header gives the wait in seconds.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying (tier_unavailable only).",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "code",
              "message"
            ],
            "additionalProperties": true,
            "properties": {
              "type": {
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "permission_error",
                  "not_found_error",
                  "conflict_error",
                  "idempotency_error",
                  "insufficient_credits_error",
                  "rate_limit_error",
                  "api_error",
                  "provider_error",
                  "service_unavailable_error"
                ]
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": "string",
                "description": "snake_case path of the offending parameter, e.g. references.images[1]"
              }
            }
          }
        }
      },
      "References": {
        "type": "object",
        "additionalProperties": false,
        "description": "Each value is an upload id (uuid) or a public https URL.",
        "properties": {
          "first_frame": {
            "type": "string"
          },
          "last_frame": {
            "type": "string"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "videos": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "audio": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateGenerationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model",
          "prompt"
        ],
        "properties": {
          "model": {
            "type": "string",
            "examples": [
              "seedance-2.5",
              "seedance-2.0",
              "seedance-2.0-fast",
              "seedance-2.0-mini",
              "seedance-1.5-pro"
            ]
          },
          "mode": {
            "enum": [
              "text-to-video",
              "image-to-video",
              "reference-to-video",
              "edit",
              "extend"
            ],
            "default": "text-to-video"
          },
          "prompt": {
            "type": "string",
            "maxLength": 30000
          },
          "duration": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 60
              },
              {
                "const": "auto"
              }
            ],
            "default": 5
          },
          "aspect_ratio": {
            "enum": [
              "1:1",
              "4:3",
              "3:4",
              "16:9",
              "9:16",
              "21:9",
              "adaptive"
            ],
            "default": "16:9"
          },
          "resolution": {
            "enum": [
              "480p",
              "720p",
              "1080p",
              "4k"
            ],
            "default": "720p"
          },
          "generate_audio": {
            "type": "boolean",
            "default": true
          },
          "output_format": {
            "enum": [
              "mp4",
              "mov"
            ],
            "default": "mp4"
          },
          "return_last_frame": {
            "type": "boolean"
          },
          "web_search": {
            "type": "boolean"
          },
          "fixed_lens": {
            "type": "boolean"
          },
          "source_generation_id": {
            "type": "string",
            "format": "uuid",
            "description": "Required for edit and extend."
          },
          "references": {
            "$ref": "#/components/schemas/References"
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "description": "Public https URL notified on completion, signed with the workspace's per-request signing secret (create it in the dashboard first)."
          },
          "metadata": {
            "type": "object",
            "maxProperties": 16,
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Up to 16 string pairs echoed back on the generation and in webhooks."
          }
        }
      },
      "GenerationAccepted": {
        "type": "object",
        "required": [
          "id",
          "object",
          "status",
          "model",
          "credits_reserved",
          "pricing_version",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "video.generation"
          },
          "status": {
            "$ref": "#/components/schemas/GenerationStatus"
          },
          "model": {
            "type": "string"
          },
          "credits_reserved": {
            "type": "integer",
            "description": "Credits held for the job. 0 when the submission could not be confirmed (error.code submit_unconfirmed): nothing is held then."
          },
          "pricing_version": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "description": "Present only when the job could not be confirmed at submission: status is failed and code is submit_unconfirmed. A generation that failed with error code submit_unconfirmed (the provider did not confirm the submission) or timeout (it did not finish in time) had its reservation released, but the provider may still deliver it (its late callback is accepted, and timed-out jobs are polled for 24 hours). If the video arrives, the generation becomes succeeded, credits_captured is charged then, and a generation.succeeded webhook is sent. Do not resubmit such a job right away, or it may render (and be charged) twice.",
            "properties": {
              "code": {
                "const": "submit_unconfirmed"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        }
      },
      "GenerationStatus": {
        "enum": [
          "queued",
          "processing",
          "succeeded",
          "failed",
          "cancelled",
          "refunded"
        ]
      },
      "Generation": {
        "type": "object",
        "required": [
          "id",
          "object",
          "status",
          "model",
          "credits_reserved",
          "outputs",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "video.generation"
          },
          "status": {
            "$ref": "#/components/schemas/GenerationStatus",
            "description": "Public status. A generation that failed with error code submit_unconfirmed (the provider did not confirm the submission) or timeout (it did not finish in time) had its reservation released, but the provider may still deliver it (its late callback is accepted, and timed-out jobs are polled for 24 hours). If the video arrives, the generation becomes succeeded, credits_captured is charged then, and a generation.succeeded webhook is sent. Do not resubmit such a job right away, or it may render (and be charged) twice."
          },
          "model": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "prompt": {
            "type": "string"
          },
          "duration": {
            "type": [
              "number",
              "null"
            ]
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ]
          },
          "aspect_ratio": {
            "type": [
              "string",
              "null"
            ]
          },
          "generate_audio": {
            "type": "boolean"
          },
          "output_format": {
            "type": "string"
          },
          "credits_reserved": {
            "type": "integer"
          },
          "credits_captured": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Final charge once settled (0 for failed/cancelled); null while running."
          },
          "pricing_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "outputs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Signed URL valid for 1 hour."
                },
                "format": {
                  "type": "string"
                },
                "duration": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "resolution": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "last_frame_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Last frame of the video as an image, when the request set return_last_frame and the model returned one: a signed URL valid for 1 hour (fetch the generation again for a fresh one), or the provider's own URL when the frame could not be copied to storage. null otherwise. Pass it (or upload it) as the first_frame of the next shot."
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "description": "Set when the job failed (code and message). submit_unconfirmed and timeout failures can still turn into a success; see status."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "webhook_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Model": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "model"
          },
          "display_name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "status": {
            "enum": [
              "active",
              "degraded"
            ]
          },
          "is_default": {
            "type": "boolean"
          },
          "ideal_for": {
            "type": "string"
          },
          "speed": {
            "enum": [
              "fast",
              "standard",
              "slow"
            ]
          },
          "typical_seconds": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "number"
            }
          },
          "modes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "text_to_video": {
                "type": "boolean"
              },
              "image_to_video": {
                "type": "boolean"
              },
              "first_last_frame": {
                "type": "boolean"
              },
              "reference_images": {
                "type": "boolean"
              },
              "reference_videos": {
                "type": "boolean"
              },
              "reference_audio": {
                "type": "boolean"
              },
              "native_audio": {
                "type": "boolean"
              },
              "edit": {
                "type": "boolean"
              },
              "extend": {
                "type": "boolean"
              },
              "return_last_frame": {
                "type": "boolean"
              },
              "web_search": {
                "type": "boolean"
              },
              "fixed_lens": {
                "type": "boolean"
              },
              "resolutions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "aspect_ratios": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "output_formats": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "min_duration": {
                "type": "integer"
              },
              "max_duration": {
                "type": "integer"
              },
              "auto_duration": {
                "type": "boolean"
              },
              "max_reference_images": {
                "type": "integer"
              },
              "max_reference_videos": {
                "type": "integer"
              },
              "max_reference_audio": {
                "type": "integer"
              },
              "min_prompt_chars": {
                "type": "integer"
              },
              "max_prompt_chars": {
                "type": "integer"
              },
              "input_limits": {
                "type": "object"
              }
            }
          },
          "pricing": {
            "type": "object",
            "properties": {
              "pricing_version": {
                "type": "string"
              },
              "credits_per_second": {
                "type": "number"
              },
              "resolution_multiplier": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "audio_multiplier": {
                "type": "number"
              },
              "reference_video_surcharge": {
                "type": "number"
              },
              "minimum_credits": {
                "type": "number"
              },
              "usd_per_credit": {
                "type": "number"
              }
            }
          },
          "last_verified_at": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PendingUpload": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "upload"
          },
          "status": {
            "const": "pending"
          },
          "type": {
            "enum": [
              "image",
              "video",
              "audio"
            ]
          },
          "mime": {
            "type": "string"
          },
          "bytes": {
            "type": "integer"
          },
          "filename": {
            "type": "string"
          },
          "upload_url": {
            "type": "string",
            "format": "uri"
          },
          "upload_method": {
            "const": "PUT"
          },
          "upload_headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "max_bytes": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Upload": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "upload"
          },
          "status": {
            "enum": [
              "pending",
              "validating",
              "ready",
              "failed"
            ],
            "description": "pending until /complete; ready when usable as a reference. Videos, audio and images whose header could not be read stay validating while the file is probed (dimensions, duration): poll GET /uploads/{id} until ready or failed."
          },
          "type": {
            "enum": [
              "image",
              "video",
              "audio"
            ]
          },
          "mime": {
            "type": "string"
          },
          "bytes": {
            "type": "integer"
          },
          "filename": {
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "duration": {
            "type": [
              "number",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "description": "Why the upload was rejected, when status is failed (for example content_mismatch or a media-limit code).",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "TestDeliveryResult": {
        "type": "object",
        "properties": {
          "endpoint_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "delivered": {
            "type": "boolean"
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "response_ms": {
            "type": "integer"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "GenerationEvent": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "evt_…; stable across retries, use it to deduplicate."
          },
          "type": {
            "enum": [
              "generation.succeeded",
              "generation.failed",
              "generation.cancelled"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Unix seconds"
          },
          "data": {
            "type": "object",
            "properties": {
              "object": {
                "$ref": "#/components/schemas/Generation"
              }
            }
          }
        }
      },
      "PingEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "const": "ping"
          },
          "created": {
            "type": "integer"
          },
          "data": {
            "type": "object",
            "properties": {
              "object": {
                "type": "object",
                "properties": {
                  "object": {
                    "const": "ping"
                  },
                  "workspace_id": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
