{
  "openapi": "3.1.0",
  "info": {
    "title": "Revana API",
    "version": "2026-10-19",
    "description": "One API key for image and video models. Prepaid credits, 100 credits = $1; only the route that succeeds is charged. Every model in /data/models.json takes the same request shape: `input` is SeedanceInput for seedance-2.5 and ModelInput for the others, whose allowed values, defaults and prices are listed per model in /data/models.json. An unknown model id answers 422 model_not_available; a model whose routes are all down at the moment, 503 model_unavailable."
  },
  "servers": [
    {
      "url": "https://api.revana.si"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "paths": {
    "/v1/generations": {
      "post": {
        "operationId": "createGeneration",
        "summary": "Submit a generation",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Resending the same key and body returns the same generation instead of starting another. The same key with a different body is 409 idempotency_conflict."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerationRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "200": {
            "description": "An Idempotency-Key replay: the generation it created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request. Codes: invalid_request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. Codes: unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The available balance does not cover reserved_usd. Codes: insufficient_credits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account is paused, or the key cannot generate yet. Codes: account_disabled, generation_not_enabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body. Codes: idempotency_conflict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 1 MB. Codes: body_too_large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The input cannot run as asked. Nothing is charged. Codes: invalid_input, max_cost_exceeded, model_not_available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Over the per-second burst guard (honour Retry-After), or too many generations in progress. Codes: rate_limited, concurrency_limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Restarting, busy, or no route up. Retry after Retry-After with the same Idempotency-Key. Codes: restarting, busy, model_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server error. Safe to retry with the same Idempotency-Key. Codes: server_error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/generations/{id}": {
      "get": {
        "operationId": "getGeneration",
        "summary": "Get a generation",
        "parameters": [
          {
            "$ref": "#/components/parameters/id"
          }
        ],
        "responses": {
          "200": {
            "description": "The generation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. Codes: unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No generation with this id on your account. Codes: not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Over the per-second burst guard. Codes: rate_limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/generations/{id}/content": {
      "get": {
        "operationId": "getGenerationContent",
        "summary": "Download the result (the first file)",
        "parameters": [
          {
            "$ref": "#/components/parameters/id"
          }
        ],
        "responses": {
          "302": {
            "description": "A redirect to a short-lived file URL."
          },
          "200": {
            "description": "The file: a video for video models, a picture for picture models.",
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No generation with this id on your account. Codes: not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Not succeeded yet. Codes: not_ready.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/generations/{id}/content/{n}": {
      "get": {
        "operationId": "getGenerationContentN",
        "summary": "Download file n of a result with several files",
        "parameters": [
          {
            "$ref": "#/components/parameters/id"
          },
          {
            "name": "n",
            "in": "path",
            "required": true,
            "description": "2 or more: the position of the file in output.urls.",
            "schema": {
              "type": "integer",
              "minimum": 2,
              "maximum": 99
            }
          }
        ],
        "responses": {
          "302": {
            "description": "A redirect to a short-lived file URL."
          },
          "200": {
            "description": "The file: a video for video models, a picture for picture models.",
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No generation with this id on your account, or it has fewer files. Codes: not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Not succeeded yet. Codes: not_ready.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List the models you can generate with now",
        "security": [],
        "description": "No key needed. Every model /v1/generations accepts at this moment, with its tasks, options and price (the same fields as /data/models.json). A model missing here answers 422 model_not_available.",
        "responses": {
          "200": {
            "description": "The models.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "object"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "object": {
                            "const": "model"
                          },
                          "name": {
                            "type": "string"
                          },
                          "maker": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "image",
                              "video",
                              "audio"
                            ]
                          },
                          "tasks": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "options": {
                            "type": "object"
                          },
                          "price": {
                            "type": "object"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from https://revana.si/dashboard."
      }
    },
    "parameters": {
      "id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[\\w-]{1,80}$"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "GenerationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model",
          "input"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "A model id from /data/models.json.",
            "examples": [
              "seedance-2.5"
            ]
          },
          "input": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SeedanceInput"
              },
              {
                "$ref": "#/components/schemas/ModelInput"
              }
            ],
            "description": "SeedanceInput for seedance-2.5; ModelInput for every other model. Unknown fields, and values the model does not offer, are refused with 422 invalid_input before anything is charged."
          },
          "max_cost_usd": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 100,
            "default": 100,
            "description": "The most this generation may cost. Routes above it are skipped."
          },
          "max_wait_s": {
            "type": "integer",
            "minimum": 1,
            "maximum": 86400,
            "description": "Routes expected to take longer than this many seconds are skipped, and once it has passed no new route is tried. A route already running may finish (up to 40 minutes) and is charged if it delivers."
          }
        }
      },
      "SeedanceInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "prompt"
        ],
        "description": "Input for seedance-2.5.",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 30000
          },
          "resolution": {
            "enum": [
              "480p",
              "720p",
              "1080p"
            ],
            "default": "720p"
          },
          "duration": {
            "type": "integer",
            "minimum": 4,
            "maximum": 30,
            "default": 5,
            "description": "Seconds. Not with video_url."
          },
          "aspect_ratio": {
            "enum": [
              "16:9",
              "9:16",
              "1:1",
              "4:3",
              "3:4",
              "21:9",
              "adaptive"
            ],
            "default": "16:9"
          },
          "audio": {
            "type": "boolean",
            "default": false
          },
          "image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "Exact first frame. Not with reference media."
          },
          "end_image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "Last frame. Needs image_url."
          },
          "image_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 4096,
              "description": "A public https URL that downloads directly."
            },
            "minItems": 1,
            "maxItems": 30,
            "description": "Reference images."
          },
          "video_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "A 4-30 s clip to edit; the result keeps its length."
          },
          "video_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 4096,
              "description": "A public https URL that downloads directly."
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "Reference clips, 30 s in all."
          },
          "audio_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 4096,
              "description": "A public https URL that downloads directly."
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "Reference sound, 30 s in all."
          }
        }
      },
      "Generation": {
        "type": "object",
        "required": [
          "id",
          "object",
          "model",
          "status",
          "output",
          "quoted_usd",
          "cost_usd",
          "attempts",
          "error",
          "created_at",
          "completed_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "gen_3f9a1c0d2b7e4a5f6c81"
            ]
          },
          "object": {
            "const": "generation"
          },
          "model": {
            "type": "string"
          },
          "status": {
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed"
            ]
          },
          "message": {
            "type": "string",
            "description": "Only while a result is being confirmed with a provider."
          },
          "output": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "urls": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "duration_s": {
                "type": "number"
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "has_audio": {
                "type": "boolean"
              }
            },
            "description": "Null until succeeded. urls lists every file (a picture model can return several: /content, /content/2, ...). Pictures carry width and height; videos also duration_s and has_audio."
          },
          "quoted_usd": {
            "type": "number",
            "description": "The price on the route tried first."
          },
          "reserved_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Held from the balance while it runs: the dearest route that may run."
          },
          "cost_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "The charge once succeeded, 0 once failed, else null."
          },
          "attempts": {
            "type": "integer"
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ModelInput": {
        "type": "object",
        "additionalProperties": false,
        "description": "The input of every model other than seedance-2.5. Which fields and values a model takes, and its defaults, are in /data/models.json (options, tasks, price).",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 30000,
            "description": "What to generate. Required unless the model only transforms media (an upscaler)."
          },
          "negative_prompt": {
            "type": "string",
            "maxLength": 5000,
            "description": "What to avoid, on models that take it."
          },
          "image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "The start frame (image to video) or the picture to edit (image to image)."
          },
          "end_image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "The last frame, on models with first and last frame. Needs image_url."
          },
          "image_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 4096
            },
            "minItems": 1,
            "maxItems": 30,
            "description": "Reference pictures, up to the model's max_input_images."
          },
          "video_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "A clip to transform or extend, on models that take one. Its length is measured and may be billed."
          },
          "audio_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "A sound to drive the video (talking avatars, lip sync)."
          },
          "resolution": {
            "type": "string",
            "description": "One of the model's resolutions, e.g. 720p, 1080p, 1K, 2K, 4K."
          },
          "aspect_ratio": {
            "type": "string",
            "description": "One of the model's aspect ratios, e.g. 16:9, 9:16, 1:1."
          },
          "duration": {
            "type": "integer",
            "minimum": 1,
            "maximum": 300,
            "description": "Video length in seconds: one of the model's durations. Per-second models are billed for it."
          },
          "audio": {
            "type": "boolean",
            "description": "Generate sound, on models that offer it. Changes the price on some models."
          },
          "quality": {
            "type": "string",
            "description": "A quality tier, on models that price by one (e.g. low, medium, high)."
          },
          "seed": {
            "type": "integer",
            "minimum": 0,
            "maximum": 4294967295,
            "description": "A seed, on models that take one."
          }
        }
      }
    }
  }
}
