{
  "openapi": "3.1.0",
  "info": {
    "title": "NoCam API",
    "version": "1",
    "summary": "One key for AI photo, video and text models.",
    "description": "Make photos, videos and text with one API key. Start with GET /models for what's on sale, read a model's input_schema, then POST /generations. Generation is asynchronous: poll GET /generations/{id}, or send `Prefer: wait=60` to hold the call. 1 credit = $0.01. A failed generation gives its credits back. For agents: https://docs.nocamclub.com/llms.txt"
  },
  "servers": [
    {
      "url": "https://api.nocamclub.com/v1"
    }
  ],
  "externalDocs": {
    "description": "Documentation",
    "url": "https://docs.nocamclub.com"
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Models",
      "description": "What's on sale, its options and prices."
    },
    {
      "name": "Generations",
      "description": "Make a photo, a video or text."
    },
    {
      "name": "Uploads",
      "description": "Files to use in a generation."
    },
    {
      "name": "Account",
      "description": "Balance and limits."
    }
  ],
  "paths": {
    "/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "listModels",
        "summary": "List models on sale",
        "description": "Open, no key needed. Limited to 60 calls a minute per address.",
        "security": [],
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Only this kind of model.",
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video",
                "text"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The models on sale.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Model"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "rate_limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "platform_paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/models/{id}": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "getModel",
        "summary": "Get a model",
        "description": "A model's options as JSON Schema (`input_schema`), its prices and how it has been doing. Open, no key needed.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The model id, like `gpt-image-2`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The model.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "platform_paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/estimate": {
      "post": {
        "tags": [
          "Generations"
        ],
        "operationId": "estimate",
        "summary": "Check a request and get its price",
        "description": "Takes the same body as POST /generations. Nothing runs and nothing is charged.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "It's valid, and this is the price.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "400": {
            "description": "invalid_parameter or unknown_model",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_for_sale or no_route",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/generations": {
      "post": {
        "tags": [
          "Generations"
        ],
        "operationId": "createGeneration",
        "summary": "Start a generation",
        "description": "Charges the price at once. A failure later gives it back. Answers 202 with a generation to poll, or, with `Prefer: wait=N`, holds the call up to N seconds (60 at most) and answers 200 if it finished in time.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Up to 200 characters. The same key and body return the first generation instead of paying twice. Always send one when you might retry.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "description": "`wait=60` holds the call until the generation is done, up to that many seconds.",
            "schema": {
              "type": "string",
              "example": "wait=60"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerationRequest"
              },
              "example": {
                "model": "gpt-image-2",
                "input": {
                  "prompt": "A yellow taxi in the rain at night",
                  "resolution": "1K",
                  "quality": "medium",
                  "aspect_ratio": "16:9"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Finished within the wait, or a repeat of a finished one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                },
                "example": {
                  "id": "gen_01a1b37b65dc7dcd8c060f296add5cf8",
                  "object": "generation",
                  "model": "gpt-image-2",
                  "status": "succeeded",
                  "mode": "live",
                  "price": {
                    "credits": 4,
                    "usd": "0.04",
                    "charged": true
                  },
                  "params": {
                    "aspect_ratio": "3:4",
                    "resolution": "1K",
                    "quality": "medium"
                  },
                  "outputs": [
                    {
                      "type": "image",
                      "url": "https://cdn.nocamclub.com/api/8f3c.../1.png",
                      "content_type": "image/png",
                      "bytes": 1461448,
                      "width": 768,
                      "height": 1024,
                      "expires_at": "2026-11-14T00:52:00.000Z"
                    }
                  ],
                  "error": null,
                  "recovered": false,
                  "purged": false,
                  "timing": {
                    "total_ms": 31000
                  },
                  "urls": {
                    "self": "https://api.nocamclub.com/v1/generations/gen_01a1b37b65dc7dcd8c060f296add5cf8"
                  },
                  "created_at": "2026-11-07T00:51:29.000Z",
                  "finished_at": "2026-11-07T00:52:00.000Z"
                }
              }
            }
          },
          "202": {
            "description": "Accepted. Poll `urls.self`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "400": {
            "description": "invalid_parameter, unknown_model, input_refused or invalid_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key or expired_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "insufficient_credits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "spend_limit_reached, model_not_allowed, missing_scope, ip_not_allowed or account_suspended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_for_sale, no_route or idempotency_conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited or too_many_active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "platform_paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Generations"
        ],
        "operationId": "listGenerations",
        "summary": "List generations",
        "description": "Newest first. Only this account's, and only the key's mode (`live` or `test`).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/GenerationStatus"
            }
          },
          {
            "name": "model",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 to 100, 20 if not set.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "`next_cursor` of the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of generations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Generation"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/generations/{id}": {
      "get": {
        "tags": [
          "Generations"
        ],
        "operationId": "getGeneration",
        "summary": "Get a generation",
        "description": "Poll this until `status` is `succeeded`, `failed` or `canceled`. `poll_after_ms` says how often.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id from POST /generations.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The generation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                },
                "example": {
                  "id": "gen_01a1b37b65dc7dcd8c060f296add5cf8",
                  "object": "generation",
                  "model": "gpt-image-2",
                  "status": "succeeded",
                  "mode": "live",
                  "price": {
                    "credits": 4,
                    "usd": "0.04",
                    "charged": true
                  },
                  "params": {
                    "aspect_ratio": "3:4",
                    "resolution": "1K",
                    "quality": "medium"
                  },
                  "outputs": [
                    {
                      "type": "image",
                      "url": "https://cdn.nocamclub.com/api/8f3c.../1.png",
                      "content_type": "image/png",
                      "bytes": 1461448,
                      "width": 768,
                      "height": 1024,
                      "expires_at": "2026-11-14T00:52:00.000Z"
                    }
                  ],
                  "error": null,
                  "recovered": false,
                  "purged": false,
                  "timing": {
                    "total_ms": 31000
                  },
                  "urls": {
                    "self": "https://api.nocamclub.com/v1/generations/gen_01a1b37b65dc7dcd8c060f296add5cf8"
                  },
                  "created_at": "2026-11-07T00:51:29.000Z",
                  "finished_at": "2026-11-07T00:52:00.000Z"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Generations"
        ],
        "operationId": "deleteGeneration",
        "summary": "Delete a generation's files and prompt",
        "description": "Removes them now instead of when they expire. Finished generations only.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The generation.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Gone.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "object",
                    "deleted"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "const": "generation"
                    },
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_finished",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/generations/{id}/cancel": {
      "post": {
        "tags": [
          "Generations"
        ],
        "operationId": "cancelGeneration",
        "summary": "Cancel a generation",
        "description": "Works while no model has it yet, and gives the credits back. After that it finishes and is billed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The generation.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Canceled, credits back.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_cancelable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/uploads": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "createUpload",
        "summary": "Upload a file",
        "description": "Two ways. Send `{url}` and we fetch a public https file (answers with the file). Or send `{content_type, bytes}` for a link to PUT the file to, then call /uploads/{id}/complete. Files stay for 48 hours.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "url"
                    ],
                    "properties": {
                      "url": {
                        "type": "string",
                        "format": "uri"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "content_type",
                      "bytes"
                    ],
                    "properties": {
                      "content_type": {
                        "type": "string",
                        "enum": [
                          "image/png",
                          "image/jpeg",
                          "image/webp",
                          "video/mp4",
                          "video/quicktime",
                          "audio/mpeg",
                          "audio/wav"
                        ]
                      },
                      "bytes": {
                        "type": "integer",
                        "minimum": 1
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The file, or the link to put it at.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/File"
                    },
                    {
                      "$ref": "#/components/schemas/UploadLink"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "invalid_parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "file_too_large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "unsupported_media",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/uploads/{id}": {
      "get": {
        "tags": [
          "Uploads"
        ],
        "operationId": "getUpload",
        "summary": "Get a file",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The file id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/uploads/{id}/complete": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "completeUpload",
        "summary": "Finish an upload",
        "description": "Call it after the PUT. We check the file's first bytes and read a video's or audio file's length. Optional: a file is checked anyway the first time a generation uses it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The file id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ready to use.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "upload_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "file_too_large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "unsupported_media",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccount",
        "summary": "Balance and limits",
        "responses": {
          "200": {
            "description": "The account as this key sees it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "getOpenApi",
        "summary": "This document",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`nc_live_…` spends credits on real models. `nc_test_…` is the free sandbox."
      }
    },
    "schemas": {
      "GenerationStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "succeeded",
          "failed",
          "canceled"
        ]
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "invalid_request",
          "invalid_parameter",
          "unknown_model",
          "input_refused",
          "invalid_api_key",
          "expired_api_key",
          "insufficient_credits",
          "account_suspended",
          "ip_not_allowed",
          "missing_scope",
          "model_not_allowed",
          "origin_not_allowed",
          "spend_limit_reached",
          "not_found",
          "idempotency_conflict",
          "no_route",
          "not_cancelable",
          "not_finished",
          "not_for_sale",
          "upload_not_found",
          "file_too_large",
          "unsupported_media",
          "rate_limited",
          "too_many_active",
          "internal",
          "platform_paused",
          "content_refused",
          "bad_input_file",
          "unsupported_request",
          "prompt_too_long",
          "output_limit",
          "timed_out",
          "provider_unavailable",
          "generation_failed",
          "sandbox_failure",
          "canceled"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "docs_url"
            ],
            "additionalProperties": true,
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string",
                "description": "What happened, for a person or an agent to read."
              },
              "path": {
                "type": "string",
                "description": "The field at fault, like `input.aspect_ratio`."
              },
              "hint": {
                "type": "string",
                "description": "What to do about it."
              },
              "retryable": {
                "type": "boolean",
                "description": "The same request can work, after any wait the error names. False: change the request, or the key or account."
              },
              "request_id": {
                "type": "string"
              },
              "docs_url": {
                "type": "string",
                "description": "A page about this code."
              }
            }
          }
        }
      },
      "GenerationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model",
          "input"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "A model id from GET /models."
          },
          "input": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "prompt"
            ],
            "description": "Which fields a model takes, and their values, differ: read its `input_schema`. An unknown field is refused, not ignored.",
            "properties": {
              "prompt": {
                "type": "string",
                "minLength": 1,
                "maxLength": 10000,
                "description": "What to make. Refer to files by order: img1, img2, video1, audio1."
              },
              "aspect_ratio": {
                "type": "string"
              },
              "resolution": {
                "type": "string"
              },
              "quality": {
                "type": "string"
              },
              "output_format": {
                "type": "string",
                "enum": [
                  "png",
                  "jpeg"
                ]
              },
              "duration": {
                "type": "integer",
                "minimum": 1,
                "description": "Clip length in seconds."
              },
              "reference_mode": {
                "type": "string",
                "enum": [
                  "ingredients",
                  "frames",
                  "start"
                ],
                "description": "How photos are used in a clip. ingredients: the model takes what it needs from them. frames: the first (and last) frame. start: the first photo opens the clip, the rest are ingredients."
              },
              "images": {
                "type": "array",
                "maxItems": 30,
                "items": {
                  "type": "string"
                },
                "description": "File ids from /uploads, or public https addresses."
              },
              "videos": {
                "type": "array",
                "maxItems": 10,
                "items": {
                  "type": "string"
                }
              },
              "audios": {
                "type": "array",
                "maxItems": 10,
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "end_user": {
            "type": "string",
            "maxLength": 100,
            "description": "Your own id for your customer: a pseudonym, never an email."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Your notes. They come back with the generation."
          },
          "retention_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 168,
            "description": "Remove the results sooner than the default 7 days."
          }
        }
      },
      "Output": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "type",
              "url",
              "content_type",
              "bytes"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "image",
                  "video"
                ]
              },
              "url": {
                "type": "string",
                "description": "A public link at an address no one can guess. It stops working at `expires_at`: copy what you want to keep."
              },
              "content_type": {
                "type": "string"
              },
              "bytes": {
                "type": "integer"
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "duration_seconds": {
                "type": "number"
              },
              "has_audio": {
                "type": "boolean"
              },
              "expires_at": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "text"
            ],
            "properties": {
              "type": {
                "const": "text"
              },
              "text": {
                "type": "string"
              }
            }
          }
        ]
      },
      "Generation": {
        "type": "object",
        "required": [
          "id",
          "object",
          "model",
          "status",
          "mode",
          "price",
          "params",
          "outputs",
          "error",
          "recovered",
          "purged",
          "timing",
          "urls",
          "created_at",
          "finished_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "generation"
          },
          "model": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/GenerationStatus"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "price": {
            "type": "object",
            "required": [
              "credits",
              "usd",
              "charged"
            ],
            "properties": {
              "credits": {
                "type": "integer"
              },
              "usd": {
                "type": "string"
              },
              "charged": {
                "type": "boolean",
                "description": "False once a failure or a cancel gave the credits back, and always on a test key."
              }
            }
          },
          "params": {
            "type": "object",
            "description": "The settings it runs with, defaults filled in.",
            "additionalProperties": true
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Output"
            }
          },
          "error": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "code",
                  "message",
                  "retryable",
                  "docs_url"
                ],
                "properties": {
                  "code": {
                    "$ref": "#/components/schemas/ErrorCode"
                  },
                  "message": {
                    "type": "string",
                    "description": "What happened."
                  },
                  "hint": {
                    "type": "string",
                    "description": "What to do about it."
                  },
                  "retryable": {
                    "type": "boolean",
                    "description": "The same request, with a new Idempotency-Key, can work."
                  },
                  "docs_url": {
                    "type": "string"
                  }
                }
              }
            ]
          },
          "recovered": {
            "type": "boolean",
            "description": "The model finished after we had given up and refunded: the result came anyway, free of charge."
          },
          "purged": {
            "type": "boolean",
            "description": "The files and prompt have been removed."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "end_user": {
            "type": "string"
          },
          "timing": {
            "type": "object",
            "required": [
              "total_ms"
            ],
            "properties": {
              "total_ms": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "urls": {
            "type": "object",
            "required": [
              "self"
            ],
            "properties": {
              "self": {
                "type": "string"
              },
              "cancel": {
                "type": "string"
              }
            }
          },
          "poll_after_ms": {
            "type": "integer",
            "description": "Only while it's in progress."
          },
          "created_at": {
            "type": "string"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Estimate": {
        "type": "object",
        "required": [
          "object",
          "valid",
          "model",
          "params",
          "credits",
          "usd"
        ],
        "properties": {
          "object": {
            "const": "estimate"
          },
          "valid": {
            "const": true
          },
          "model": {
            "type": "string"
          },
          "params": {
            "type": "object",
            "description": "The settings it would run with, defaults filled in, spelled as in a request.",
            "additionalProperties": true
          },
          "credits": {
            "type": "integer"
          },
          "usd": {
            "type": "string"
          }
        }
      },
      "Model": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "kind",
          "input_schema",
          "prices",
          "routes",
          "no_fallback",
          "stats"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "model"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "text"
            ],
            "description": "`image` means photos."
          },
          "family": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "input_schema": {
            "type": "object",
            "description": "JSON Schema of this model's `input`, limited to what the routes on sale can take."
          },
          "prices": {
            "type": "array",
            "description": "Every combination that can be bought right now.",
            "items": {
              "type": "object",
              "required": [
                "credits",
                "usd"
              ],
              "properties": {
                "resolution": {
                  "type": "string"
                },
                "quality": {
                  "type": "string"
                },
                "duration": {
                  "type": "integer"
                },
                "with_video": {
                  "type": "boolean",
                  "description": "This price applies when a video is among the files."
                },
                "credits": {
                  "type": "integer"
                },
                "usd": {
                  "type": "string"
                }
              }
            }
          },
          "routes": {
            "type": "integer",
            "description": "How many routes can serve it. A failed route hands the order to the next."
          },
          "no_fallback": {
            "type": "boolean",
            "description": "Only one route: if it's down, the model is down."
          },
          "stats": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "window_days",
                  "median_seconds",
                  "p90_seconds",
                  "success_rate",
                  "samples"
                ],
                "properties": {
                  "window_days": {
                    "type": "integer"
                  },
                  "median_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "p90_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "success_rate": {
                    "type": "number"
                  },
                  "samples": {
                    "type": "integer"
                  }
                }
              }
            ],
            "description": "Orders through the API in the last 7 days. Null until there are some."
          }
        }
      },
      "File": {
        "type": "object",
        "required": [
          "id",
          "object",
          "kind",
          "content_type",
          "status",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "file"
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio"
            ]
          },
          "content_type": {
            "type": "string"
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "duration_seconds": {
            "type": "number",
            "description": "Video and audio, when the file's headers say."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "ready"
            ]
          },
          "expires_at": {
            "type": "string"
          }
        }
      },
      "UploadLink": {
        "allOf": [
          {
            "$ref": "#/components/schemas/File"
          },
          {
            "type": "object",
            "required": [
              "upload_url",
              "upload_method",
              "upload_headers"
            ],
            "properties": {
              "upload_url": {
                "type": "string",
                "description": "PUT the file here within 15 minutes."
              },
              "upload_method": {
                "const": "PUT"
              },
              "upload_headers": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          }
        ]
      },
      "Account": {
        "type": "object",
        "required": [
          "object",
          "mode",
          "level",
          "credits",
          "limits",
          "top_up_url"
        ],
        "properties": {
          "object": {
            "const": "account"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "level": {
            "type": "string",
            "enum": [
              "sandbox",
              "start",
              "build",
              "scale",
              "custom"
            ]
          },
          "credits": {
            "type": "object",
            "required": [
              "spendable_on_models",
              "bonus"
            ],
            "properties": {
              "spendable_on_models": {
                "type": "integer"
              },
              "bonus": {
                "type": "integer",
                "description": "For scenes, not for models directly."
              }
            }
          },
          "limits": {
            "type": "object",
            "required": [
              "per_minute",
              "in_progress",
              "credits_per_day"
            ],
            "properties": {
              "per_minute": {
                "type": "integer"
              },
              "in_progress": {
                "type": "integer"
              },
              "credits_per_day": {
                "type": "integer"
              }
            }
          },
          "top_up_url": {
            "type": "string"
          }
        }
      }
    }
  }
}
