{
  "openapi": "3.1.0",
  "info": {
    "title": "AI Content Drop API",
    "version": "2.4.0",
    "summary": "Generate AI video and images programmatically, and read AI Content Drop's public content as plain text, markdown, or XML.",
    "description": "The public REST API of https://aicontentdrop.com — generate AI video and images programmatically on your own account.\n\n**Authentication**: an API key created in your dashboard (Settings → Integrations). Keys look like `acd_live_…` and are sent as `Authorization: Bearer`. The index and the model catalogue need no key at all; everything that touches an account or spends credits does.\n\n**Generation is submit-and-poll**: POST returns a record immediately with status `generating`, then GET /v1/videos/{id} until `completed`. Credits are flat per model and are only charged on success — safety blocks, provider failures, and timeouts cost nothing.\n\n**Rate limits**: 120 requests/minute. Every response carries `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset`; a 429 additionally sends `Retry-After` (seconds).\n\n**Errors** are always structured JSON, never HTML. /v1 read endpoints return `{\"error\": {\"code\", \"message\"}}`; the generation endpoints (shared with the website) return `{\"error\": \"<message>\", \"code\"?: \"<CODE>\"}`.\n\n**Versioning**: the version is in the URL path (`/v1`). A breaking change ships as a new path segment; the previous version keeps working for at least 180 days and is announced with `Deprecation` and `Sunset` response headers (RFC 9745 / RFC 8594) before it is removed. Policy: https://aicontentdrop.com/developers#versioning\n\n**Other protocols over the same capabilities**: MCP (Streamable HTTP) at POST /mcp, manifest at /.well-known/mcp.json; A2A (JSON-RPC 2.0) at POST /a2a; NLWeb at POST /ask. The read-only text/markdown content surface is documented separately at https://aicontentdrop.com/openapi-content.json.\n\n**Batch**: `POST /v1/batch` runs up to 20 read operations in one request; `POST /v1/models/cost` quotes up to 50 model/quantity pairs. Generation is never batched — see the endpoint description for why.\n\n**Sandbox**: send `X-Sandbox: true` on either generation endpoint, with or without a key, to rehearse the call for free.\n\n**GraphQL**: the same data is served at `POST https://aicontentdrop.com/graphql` with open introspection, Relay connections, and typed errors. SDL at https://aicontentdrop.com/graphql/schema.graphql, documentation at https://aicontentdrop.com/docs/graphql.",
    "contact": {
      "name": "AI Content Drop support",
      "email": "support@aicontentdrop.com",
      "url": "https://aicontentdrop.com/contact"
    },
    "license": {
      "name": "Content usage terms",
      "url": "https://aicontentdrop.com/terms-of-use"
    }
  },
  "servers": [
    {
      "url": "https://aicontentdrop.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Developer documentation, versioning and deprecation policy",
    "url": "https://aicontentdrop.com/docs/versioning"
  },
  "tags": [
    {
      "name": "account",
      "description": "API-key identity and credit balance"
    },
    {
      "name": "models",
      "description": "Model catalogue and credit-cost estimates"
    },
    {
      "name": "generation",
      "description": "Start and poll video/image generations (submit-and-poll)"
    },
    {
      "name": "discovery",
      "description": "Unauthenticated index and capability discovery"
    },
    {
      "name": "batch",
      "description": "Run many read operations in one request instead of looping."
    }
  ],
  "paths": {
    "/v1": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getApiIndex",
        "summary": "Machine-readable API index",
        "description": "Unauthenticated index of the /v1 surface: endpoint list, authentication scheme, documentation URL, and the MCP endpoint. The starting point for an agent that only knows the domain.",
        "responses": {
          "200": {
            "description": "API index",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "documentation": {
                      "type": "string",
                      "format": "uri"
                    },
                    "authentication": {
                      "type": "string"
                    },
                    "endpoints": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    },
                    "mcp": {
                      "type": "object",
                      "properties": {
                        "endpoint": {
                          "type": "string",
                          "format": "uri"
                        },
                        "transport": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Read Retry-After (header) or error.retry_after_seconds (body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": []
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "getAccount",
        "summary": "Account and credit balance",
        "description": "Identity, plan, and remaining credits for the account that owns the API key, plus the key's own name, scopes, and optional daily credit ceiling. The first call every integration should make — it verifies the key and tells you how much budget is available.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account details",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "credits",
                    "plan"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "username": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "email"
                    },
                    "credits": {
                      "type": "number",
                      "description": "Remaining credit balance"
                    },
                    "plan": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "free | starter | professional | ultra | enterprise_max"
                    },
                    "api_key": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "daily_credit_limit": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Max credits this key may spend per UTC day; null = unlimited"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "tags": [
          "models"
        ],
        "operationId": "listModels",
        "summary": "Model catalogue with credit costs",
        "description": "Every generation model available on the platform with its flat per-generation credit cost. Model IDs use underscores (e.g. `kling_3_0`, `veo_3_fast`); dashed forms are accepted everywhere and normalized. No API key required; sending one is accepted and simply identifies the caller.",
        "security": [
          {},
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Catalogue to list",
            "schema": {
              "type": "string",
              "enum": [
                "video",
                "image"
              ],
              "default": "video"
            }
          },
          {
            "name": "max_credits",
            "in": "query",
            "description": "Only models costing at most this many credits per generation",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Model catalogue",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "type",
                    "count",
                    "models"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "enum": [
                        "video",
                        "image"
                      ]
                    },
                    "count": {
                      "type": "integer"
                    },
                    "note": {
                      "type": "string"
                    },
                    "models": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "credits"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Model ID to pass to the generation endpoints"
                          },
                          "name": {
                            "type": "string"
                          },
                          "credits": {
                            "type": "number",
                            "description": "Flat credit cost per generation"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/models/{id}/cost": {
      "get": {
        "tags": [
          "models"
        ],
        "operationId": "estimateModelCost",
        "summary": "Credit cost estimate for a model",
        "description": "Quote the credit cost of one or more generations with a model before committing. Dashed IDs are normalized to underscores. No API key required; sending one is accepted and simply identifies the caller.",
        "security": [
          {},
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Model ID, e.g. kling_3_0",
            "schema": {
              "type": "string"
            },
            "example": "kling_3_0"
          },
          {
            "name": "quantity",
            "in": "query",
            "description": "Number of generations to quote",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cost estimate",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "model_id",
                    "credits_each",
                    "credits_total"
                  ],
                  "properties": {
                    "model_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "credits_each": {
                      "type": "number"
                    },
                    "quantity": {
                      "type": "integer"
                    },
                    "credits_total": {
                      "type": "number"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "video",
                        "image"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/generate/video": {
      "post": {
        "tags": [
          "generation"
        ],
        "operationId": "generateVideo",
        "summary": "Start a video generation",
        "description": "Submits a video generation and returns immediately with status `generating`. Poll GET /v1/videos/{id} (every ~5 seconds) until status is `completed` (video_url populated) or `failed`. Credits are deducted only on success. This endpoint shares its implementation with the website, so safety checks, model fallback, and credit accounting are identical; its error shape is `{\"error\": \"<message>\"}`. \n\n**Async contract:** returns 202 with a `Location` header pointing at the poll URL; poll every ~5 seconds until `status` is `completed` or `failed`. **Idempotency:** send `Idempotency-Key` so a retry cannot double-charge. **Sandbox:** send `X-Sandbox: true` to rehearse without spending credits.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "prompt"
                ],
                "properties": {
                  "prompt": {
                    "type": "string",
                    "description": "What to generate, in natural language"
                  },
                  "aiModel": {
                    "type": "string",
                    "description": "Model ID from /v1/models (e.g. kling_3_0). Omit to let the platform choose.",
                    "example": "kling_3_0"
                  },
                  "duration": {
                    "type": "number",
                    "description": "Clip length in seconds (model-dependent caps apply)",
                    "default": 5
                  },
                  "aspectRatio": {
                    "type": "string",
                    "description": "16:9, 9:16, or 1:1",
                    "default": "16:9"
                  },
                  "style": {
                    "type": "string",
                    "default": "cinematic"
                  },
                  "negativePrompt": {
                    "type": "string"
                  },
                  "imageUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional image-to-video conditioning; must be a public https:// URL"
                  },
                  "imageRefs": {
                    "type": "array",
                    "description": "Multi-image references for models that support them",
                    "items": {
                      "type": "object",
                      "required": [
                        "url"
                      ],
                      "properties": {
                        "url": {
                          "type": "string",
                          "format": "uri"
                        },
                        "role": {
                          "type": "string",
                          "description": "e.g. reference_image, first_frame, last_frame"
                        }
                      }
                    }
                  },
                  "endFrameUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Convenience for a last_frame reference"
                  },
                  "quality": {
                    "type": "string"
                  },
                  "resolution": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Poll GET /v1/videos/{id} until status is completed or failed.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Location": {
                "description": "Absolute URL to poll for this generation",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Retry-After": {
                "description": "Suggested seconds between polls",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotency-Replayed": {
                "description": "Present and `true` when this response was replayed from a previous request with the same Idempotency-Key",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/GenerationBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationError"
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key was already used with a different request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key (max 200 chars). Retrying with the same key returns the FIRST response instead of starting a second generation, so a dropped connection cannot double-charge. Reusing a key with a different body returns 409.",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "example": "a1f3c9e2-7b40-4c1a-9e77-2b1d4c8f0a55"
          },
          {
            "name": "X-Sandbox",
            "in": "header",
            "required": false,
            "description": "Set to `true` to validate the request and receive a synthetic completed generation. No provider call, no credits spent, no record created. Use it to rehearse an integration before it costs money.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/SandboxHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          },
          {
            "$ref": "#/components/parameters/SandboxQuery"
          }
        ]
      }
    },
    "/v1/generate/image": {
      "post": {
        "tags": [
          "generation"
        ],
        "operationId": "generateImage",
        "summary": "Start an image generation",
        "description": "Generates an image. Same authentication, billing, and error conventions as video generation. Model IDs come from GET /v1/models?type=image. \n\n**Async contract:** returns 202 with a `Location` header pointing at the poll URL; poll every ~5 seconds until `status` is `completed` or `failed`. **Idempotency:** send `Idempotency-Key` so a retry cannot double-charge. **Sandbox:** send `X-Sandbox: true` to rehearse without spending credits.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "prompt"
                ],
                "properties": {
                  "prompt": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string",
                    "description": "Image model ID from /v1/models?type=image",
                    "example": "nano_banana_pro"
                  },
                  "negativePrompt": {
                    "type": "string"
                  },
                  "imageSize": {
                    "type": "string"
                  },
                  "width": {
                    "type": "integer"
                  },
                  "height": {
                    "type": "integer"
                  },
                  "seed": {
                    "type": "integer"
                  },
                  "steps": {
                    "type": "integer"
                  },
                  "outputFormat": {
                    "type": "string"
                  },
                  "imageUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Source image for image-to-image models"
                  },
                  "imageUrls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Poll GET /v1/videos/{id} until status is completed or failed.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Location": {
                "description": "Absolute URL to poll for this generation",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Retry-After": {
                "description": "Suggested seconds between polls",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotency-Replayed": {
                "description": "Present and `true` when this response was replayed from a previous request with the same Idempotency-Key",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/GenerationBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationError"
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key was already used with a different request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key (max 200 chars). Retrying with the same key returns the FIRST response instead of starting a second generation, so a dropped connection cannot double-charge. Reusing a key with a different body returns 409.",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "example": "a1f3c9e2-7b40-4c1a-9e77-2b1d4c8f0a55"
          },
          {
            "name": "X-Sandbox",
            "in": "header",
            "required": false,
            "description": "Set to `true` to validate the request and receive a synthetic completed generation. No provider call, no credits spent, no record created. Use it to rehearse an integration before it costs money.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/SandboxHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          },
          {
            "$ref": "#/components/parameters/SandboxQuery"
          }
        ]
      }
    },
    "/v1/videos": {
      "get": {
        "tags": [
          "generation"
        ],
        "operationId": "listVideos",
        "summary": "List recent generations",
        "description": "Recent video generations for the account, newest first. Page by following `next_cursor` until it is null.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from a previous response`s `next_cursor`. Preferred over offset: stable while the list grows.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Legacy offset paging. Kept for existing clients; prefer `cursor`, which does not skip or duplicate rows when new generations arrive mid-scan.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "deprecated": true
          }
        ],
        "responses": {
          "200": {
            "description": "Video list",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "count",
                    "videos"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "videos": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Video"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Whether another page exists"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` to fetch the next page; null at the end"
                    },
                    "next_url": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri",
                      "description": "Ready-made URL for the next page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/videos/{id}": {
      "get": {
        "tags": [
          "generation"
        ],
        "operationId": "getVideo",
        "summary": "Poll a generation",
        "description": "The polling half of submit-and-poll. status moves generating → completed (video_url populated) or → failed (error_message populated). Videos owned by other accounts read as 404.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Video record",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/.well-known/agent-skills/index.json": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getAgentSkillsIndex",
        "summary": "Importable agent skills index",
        "description": "Agent Skills Discovery index (v0.2.0 schema): downloadable SKILL.md guides with SHA-256 digests, covering AI video prompt craft, 30-second UGC ad scripting, and AI video model selection. Artifacts live under /agent-skills/{name}.md.",
        "responses": {
          "200": {
            "description": "The published agent skills, each with a URL and a content digest.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentSkillsIndex"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Read Retry-After (header) or error.retry_after_seconds (body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "security": []
      }
    },
    "/ask": {
      "post": {
        "tags": [
          "discovery"
        ],
        "operationId": "askNaturalLanguage",
        "summary": "Natural-language query (NLWeb)",
        "description": "Ask a natural-language question about the model catalogue or our published guides and get Schema.org-shaped JSON results. Implements the NLWeb /ask convention (github.com/microsoft/NLWeb). Read-only, no API key, keyword-routed (no LLM call), 60 requests/minute. Use it for discovery; use POST /v1/generate/video to actually generate. Set `prefer.streaming: true` (or send `Accept: text/event-stream`) for an SSE response with `start`, `result`, and `complete` events.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "The question, in natural language",
                    "example": "which video models cost under 15 credits?"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked results",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "_meta",
                    "query",
                    "results"
                  ],
                  "properties": {
                    "_meta": {
                      "type": "object",
                      "required": [
                        "response_type",
                        "version"
                      ],
                      "properties": {
                        "response_type": {
                          "type": "string",
                          "enum": [
                            "model_catalogue",
                            "articles"
                          ],
                          "description": "Which answer set the query was routed to"
                        },
                        "version": {
                          "type": "string",
                          "description": "NLWeb contract version"
                        },
                        "site": {
                          "type": "string"
                        },
                        "protocol": {
                          "type": "string"
                        },
                        "documentation": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    },
                    "query": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "url",
                          "name",
                          "site",
                          "score",
                          "description"
                        ],
                        "properties": {
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "name": {
                            "type": "string"
                          },
                          "site": {
                            "type": "string"
                          },
                          "score": {
                            "type": "number",
                            "description": "Relevance, 0-100"
                          },
                          "description": {
                            "type": "string"
                          },
                          "schema_object": {
                            "type": "object",
                            "description": "Schema.org representation of the item",
                            "additionalProperties": true
                          }
                        }
                      }
                    },
                    "capabilities": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE frames: event: start | result | complete"
                }
              }
            }
          },
          "400": {
            "description": "The query was missing or unusable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "askNaturalLanguageGet",
        "summary": "Natural-language query (NLWeb, query-string form)",
        "description": "Identical to POST /ask, with the question supplied as the `query` query-string parameter. Provided because some agent HTTP clients cannot send a request body on a discovery call.",
        "security": [],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "The question, in natural language",
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "example": "which video models cost under 15 credits?"
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "_meta",
                    "query",
                    "results"
                  ],
                  "properties": {
                    "_meta": {
                      "type": "object",
                      "required": [
                        "response_type",
                        "version"
                      ],
                      "properties": {
                        "response_type": {
                          "type": "string",
                          "enum": [
                            "model_catalogue",
                            "articles"
                          ],
                          "description": "Which answer set the query was routed to"
                        },
                        "version": {
                          "type": "string",
                          "description": "NLWeb contract version"
                        },
                        "site": {
                          "type": "string"
                        },
                        "protocol": {
                          "type": "string"
                        },
                        "documentation": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    },
                    "query": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "url",
                          "name",
                          "site",
                          "score",
                          "description"
                        ],
                        "properties": {
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "name": {
                            "type": "string"
                          },
                          "site": {
                            "type": "string"
                          },
                          "score": {
                            "type": "number",
                            "description": "Relevance, 0-100"
                          },
                          "description": {
                            "type": "string"
                          },
                          "schema_object": {
                            "type": "object",
                            "description": "Schema.org representation of the item",
                            "additionalProperties": true
                          }
                        }
                      }
                    },
                    "capabilities": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The query was missing or unusable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/models/cost": {
      "post": {
        "tags": [
          "models"
        ],
        "operationId": "estimateCostBatch",
        "summary": "Cost estimate for many models at once",
        "description": "Quote up to 50 model/quantity pairs in one request. Read-only — it never starts a generation. An agent planning a batch would otherwise loop the single-model endpoint and spend its rate-limit budget on arithmetic. A bad line returns a per-item error instead of failing the whole batch.",
        "security": [
          {},
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "required": [
                        "model_id"
                      ],
                      "properties": {
                        "model_id": {
                          "type": "string",
                          "example": "kling_3_0"
                        },
                        "quantity": {
                          "type": "integer",
                          "minimum": 1,
                          "default": 1
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-item quotes plus the batch total",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "count",
                    "credits_total",
                    "items"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "credits_total": {
                      "type": "number",
                      "description": "Sum across every item that resolved"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer"
                          },
                          "model_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "credits_each": {
                            "type": "number"
                          },
                          "quantity": {
                            "type": "integer"
                          },
                          "credits_total": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "type": {
                            "type": "string"
                          },
                          "error": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "properties": {
                              "code": {
                                "type": "string"
                              },
                              "message": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or oversized items array",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agent/auth/register": {
      "post": {
        "tags": [
          "account"
        ],
        "operationId": "registerAgent",
        "summary": "Register an agent for a read-scoped token",
        "description": "Self-registration for unattended agents (WorkOS auth.md `agent_auth`, identity type `anonymous`). Returns a client_id/client_secret and an immediately usable read-scoped bearer token. The token only raises rate limits on the public read surface — it cannot generate, spend credits, or read an account. Generation needs an `acd_live_` key created by a signed-in human.",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_name": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "How you want to be identified in our logs"
                  },
                  "identity_type": {
                    "type": "string",
                    "enum": [
                      "anonymous"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. The response already contains a usable read-scope bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRegistration"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported identity_type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agent/auth/claim": {
      "post": {
        "tags": [
          "account"
        ],
        "operationId": "claimAgentToken",
        "summary": "Exchange client credentials for a fresh token",
        "description": "Trade the client_id and client_secret from registration for a new 24-hour read-scoped bearer token. Use this when a token expires rather than registering again.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "client_secret"
                ],
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A refreshed read-scope bearer token for an existing client.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentTokenClaim"
                }
              }
            }
          },
          "401": {
            "description": "Unknown client_id or wrong client_secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agent/auth/revoke": {
      "post": {
        "tags": [
          "account"
        ],
        "operationId": "revokeAgentToken",
        "summary": "Revoke an agent token",
        "description": "Invalidate a read-scoped agent token immediately. Per RFC 7009, revoking an already-invalid token is a success, not an error.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No token supplied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Read Retry-After (header) or error.retry_after_seconds (body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/batch": {
      "post": {
        "tags": [
          "batch"
        ],
        "operationId": "runBatch",
        "summary": "Run several read operations in one request",
        "description": "Dispatches up to 20 GET operations against the public read surface and returns their results in one envelope, so an agent acting on many items does not spend one request per item.\n\n**Reads only.** Generation is deliberately not batchable: a partial failure in a batch of money-spending calls leaves credits nobody can account for, so each generation keeps its own request and its own `Idempotency-Key`.\n\n**Per-item status.** An operation that fails carries its own `status` and `error`; the envelope is still 200. No API key is required for the paths it accepts.",
        "security": [
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "operations"
                ],
                "properties": {
                  "operations": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "description": "The operations to run, in order.",
                    "items": {
                      "type": "object",
                      "required": [
                        "method",
                        "path"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Your label for this operation, echoed in the result. Defaults to the array index."
                        },
                        "method": {
                          "type": "string",
                          "enum": [
                            "GET"
                          ],
                          "description": "Only GET is accepted."
                        },
                        "path": {
                          "type": "string",
                          "description": "A read path: /v1/models or /v1/models/{id}/cost, with an optional query string.",
                          "example": "/v1/models/kling_3_0/cost?quantity=3"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every operation was dispatched. Inspect each result for its own status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Operations received."
                    },
                    "succeeded": {
                      "type": "integer",
                      "description": "Operations that returned 200."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "integer",
                            "description": "HTTP status this operation would have returned on its own."
                          },
                          "body": {
                            "type": "object",
                            "nullable": true,
                            "description": "The operation result, or null when it failed."
                          },
                          "error": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "code": {
                                "type": "string"
                              },
                              "message": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No operations, more than 20, or a malformed envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Read Retry-After (header) or error.retry_after_seconds (body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/auth/register": {
      "post": {
        "operationId": "initiateSignup",
        "summary": "Start account creation for a human",
        "description": "Sends the given address a sign-in link and returns 202. The account is not usable until the human clicks it — that click is what confirms the mailbox and releases the 10 free credits, and it is the one step an agent cannot perform. There is deliberately NO password parameter: an agent should never hold a user's password. An address that already has an account gets the same response, so this cannot be used to test who is registered. Rate limited to 5 per hour per IP.",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "The HUMAN's email address. They must be able to open it."
                  }
                }
              },
              "examples": {
                "default": {
                  "value": {
                    "email": "person@example.com"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Sign-in link sent. Tell the human to click it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignupInitiated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The sign-in email could not be sent. The human can sign up at /register.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "acd_live_…",
        "description": "API key created at https://aicontentdrop.com/settings/integrations (any account, free tier included). Sent as `Authorization: Bearer acd_live_…`. Keys are shown once at creation and may carry a daily credit ceiling."
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Requests allowed in the current window (120/minute on /v1)",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in the current window",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the current window resets",
        "schema": {
          "type": "integer"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "Every failure on the agent-facing surfaces (/v1, /graphql, /ask, /agent, /mcp) uses this envelope, on every status code. An HTML error page from these paths is a bug.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code. Branch on this, never on the message text.",
                "enum": [
                  "invalid_request",
                  "invalid_json",
                  "payload_too_large",
                  "unauthorized",
                  "unauthenticated",
                  "invalid_client",
                  "forbidden",
                  "not_found",
                  "unknown_endpoint",
                  "unknown_model",
                  "method_not_allowed",
                  "unsupported_operation",
                  "unsupported_identity_type",
                  "missing_query",
                  "idempotency_conflict",
                  "insufficient_credits",
                  "safety_rejected",
                  "rate_limited",
                  "query_too_large",
                  "operation_failed",
                  "provider_error",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation that names the failing field or resource, and what to do next."
              },
              "retry_after_seconds": {
                "type": "integer",
                "minimum": 1,
                "description": "Present on 429. Mirrors the Retry-After header. Wait this long before retrying; retrying sooner extends the window."
              },
              "documentation": {
                "type": "string",
                "format": "uri",
                "description": "URL explaining this class of error."
              },
              "field": {
                "type": "string",
                "description": "Present on validation failures: the request field at fault."
              }
            },
            "additionalProperties": true
          }
        },
        "examples": [
          {
            "error": {
              "code": "rate_limited",
              "message": "Rate limit exceeded. Wait 34 seconds and retry.",
              "retry_after_seconds": 34,
              "documentation": "https://aicontentdrop.com/docs/rate-limits"
            }
          }
        ]
      },
      "GenerationError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message"
          },
          "code": {
            "type": "string",
            "description": "Optional machine-readable code, e.g. INVALID_BILLING_MODE"
          }
        },
        "description": "Error shape of the generation endpoints (shared with the website UI)."
      },
      "Video": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "generating | completed | failed | timeout"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          },
          "prompt": {
            "type": [
              "string",
              "null"
            ]
          },
          "duration": {
            "type": [
              "number",
              "null"
            ]
          },
          "aspect_ratio": {
            "type": [
              "string",
              "null"
            ]
          },
          "credits_used": {
            "type": "number"
          },
          "video_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Populated when status is completed"
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Populated when status is failed"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "RateLimitError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          }
        ],
        "description": "429. Read Retry-After (header) or error.retry_after_seconds (body) and back off; they carry the same number."
      },
      "GenerationJob": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "description": "A submitted generation. Submit-and-poll: this is the acknowledgement, not the result. Poll GET /v1/videos/{id} until status is completed or failed.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Poll this id. A sandbox job is prefixed `sandbox_`."
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "generating",
              "completed",
              "failed",
              "timeout"
            ]
          },
          "aiModel": {
            "type": [
              "string",
              "null"
            ],
            "description": "Normalised model id the job was routed to."
          },
          "creditsUsed": {
            "type": "number",
            "description": "0 at submission. Billing is post-deduct: credits are charged only when the generation succeeds."
          },
          "videoUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "GenerationAccepted": {
        "type": "object",
        "required": [
          "video"
        ],
        "description": "202 from either generation endpoint. `video` carries the job for images too — one shape across both so a client needs one parser.",
        "properties": {
          "sandbox": {
            "type": "boolean",
            "description": "true when X-Sandbox: true was sent. Nothing was generated and no credits were spent."
          },
          "video": {
            "$ref": "#/components/schemas/GenerationJob"
          }
        }
      },
      "AgentRegistration": {
        "type": "object",
        "required": [
          "client_id",
          "access_token",
          "token_type",
          "expires_in",
          "scope"
        ],
        "description": "201 from POST /agent/auth/register. One unauthenticated call returns a usable read-scope bearer token — no email, no human, no approval.",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string",
            "description": "Shown once. Needed only to claim the client against a real account later."
          },
          "client_name": {
            "type": "string"
          },
          "identity_type": {
            "type": "string",
            "enum": [
              "anonymous"
            ]
          },
          "access_token": {
            "type": "string",
            "description": "Send as `Authorization: Bearer <token>`."
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds until expiry (86400)."
          },
          "expires_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "scope": {
            "type": "string",
            "enum": [
              "read"
            ],
            "description": "Read only. This token cannot spend credits."
          },
          "claim_uri": {
            "type": "string",
            "format": "uri"
          },
          "revocation_uri": {
            "type": "string",
            "format": "uri"
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "note": {
            "type": "string"
          }
        }
      },
      "AgentTokenClaim": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "scope"
        ],
        "description": "200 from POST /agent/auth/claim — a refreshed token for an already-registered client.",
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer"
          },
          "expires_at": {
            "type": "integer"
          },
          "scope": {
            "type": "string",
            "enum": [
              "read"
            ]
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "AgentSkill": {
        "type": "object",
        "required": [
          "name",
          "type",
          "description",
          "url"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "skill-md"
            ]
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "digest": {
            "type": "string",
            "description": "sha256:… of the skill document, so a client can cache and detect changes."
          }
        }
      },
      "AgentSkillsIndex": {
        "type": "object",
        "required": [
          "skills"
        ],
        "description": "Agent Skills Discovery 0.2.0 index.",
        "properties": {
          "$schema": {
            "type": "string",
            "format": "uri"
          },
          "skills": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentSkill"
            }
          }
        }
      },
      "SignupInitiated": {
        "type": "object",
        "required": [
          "status",
          "email",
          "message_for_human",
          "next_step",
          "free_credits"
        ],
        "description": "202 from POST /v1/auth/register. The account is NOT usable yet — a human must click the emailed link. Relay `message_for_human` to your user rather than reporting success.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "human_action_required"
            ]
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "message_for_human": {
            "type": "string",
            "description": "Written to be repeated verbatim to the person you are acting for."
          },
          "next_step": {
            "type": "object",
            "properties": {
              "actor": {
                "type": "string",
                "enum": [
                  "human"
                ]
              },
              "action": {
                "type": "string"
              },
              "why": {
                "type": "string"
              },
              "fallback_url": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "free_credits": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer"
              },
              "granted_when": {
                "type": "string"
              },
              "granted_now": {
                "type": "boolean",
                "description": "Always false here. Credits are released on email confirmation."
              },
              "card_required": {
                "type": "boolean"
              }
            }
          },
          "available_without_an_account": {
            "type": "object",
            "description": "Surfaces usable immediately, so a blocked journey continues instead of stopping.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "after_verification": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, revoked, or budget-exhausted API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such resource (or not owned by this account)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limited. Read Retry-After (header) or error.retry_after_seconds (body) — they carry the same number — and wait that long. Retrying sooner extends the window rather than shortening it.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      },
      "GenerationBadRequest": {
        "description": "Invalid request (missing prompt, bad imageUrl, unknown model, …)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenerationError"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected failure on our side. Retry once; nothing was charged.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "The request was malformed. `error.code` names the class of problem and `error.message` names the field.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "parameters": {
      "SandboxHeader": {
        "name": "X-Sandbox",
        "in": "header",
        "required": false,
        "description": "Send `true` to rehearse this call. Returns a synthetic completed generation: no provider call, no credits, no history row. Validation still runs. Works without an API key, so the response shape is observable before a credential exists. Documented at https://aicontentdrop.com/docs/sandbox.",
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "1"
          ]
        },
        "example": "true"
      },
      "IdempotencyKeyHeader": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Client-generated key (max 200 characters). A retry with the same key returns the FIRST response instead of starting a second generation. The same key with a different body is a 409. Keys are retained 24 hours.",
        "schema": {
          "type": "string",
          "maxLength": 200
        },
        "example": "9f1c2b7e-0f2a-4e0b-9a1e-3c9f5d2b7a10"
      },
      "SandboxQuery": {
        "name": "sandbox",
        "in": "query",
        "required": false,
        "description": "Query-parameter form of the X-Sandbox header, for clients that cannot set headers.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    }
  },
  "x-api-version": "2026-08-24",
  "x-versioning": {
    "strategy": "url-path",
    "current": "v1",
    "version_header": "API-Version",
    "deprecation_policy": "https://aicontentdrop.com/docs/versioning",
    "deprecation_headers": [
      "Deprecation",
      "Sunset",
      "Link; rel=\"deprecation\""
    ],
    "minimum_notice_days": 180,
    "breaking_changes": "Ship at a new path (/v2). /v1 response shapes are additive only."
  }
}
