{
  "openapi": "3.1.0",
  "info": {
    "title": "SlyOS Developer API",
    "version": "v1",
    "description": "Turn any inbox you own — a helpdesk, a ticketing system, your own app’s chat — into a SlyOS channel. Send messages in; SlyOS writes the replies with the account’s own Brain: what the business has taught it, the voice it has learned, and the conversation so far. You send each reply through your own system.\n\n## Authentication\nEvery request sends `Authorization: Bearer <key>`. Keys starting `sly_test_` are the sandbox: free, and they reach nobody. Keys starting `sly_live_` are live. A sandbox key creates and lists sandbox channels; a live key, live ones — and neither can reach the other's messages, cards or replies: an id from the other environment is a `404`. Sandbox drafts are free and limited to 50 in any 24 hours. Each operation names the scope its key must carry, and a key without it is refused `403 missing_scope`. A missing, malformed or revoked key is refused `401 unauthorized`, always with the same sentence.\n\n## What costs money\nOnly `POST /v1/developer/drafts`, only on a live key, and only when a reply is produced:\n- From 2026-09-01: 10¢ per AI response (price version `2026-09-01`)\n\nTaking messages in, reading replies back, reporting deliveries, usage and wallet lookups are free. So is everything in the sandbox. A reply that is refused, fails, or is judged unnecessary is not charged.\n\n## Retrying safely\n- `POST /v1/developer/messages`: the same `external_message_id` on the same channel is stored once; the repeat answers `200` with `duplicate: true`.\n- `POST /v1/developer/drafts`: `generation_id` is the idempotency key for the charge, and it is yours: unique within your account and the key’s environment. Derive it from the `message_id` SlyOS returned — `<message_id>:reply-1` — so it is unique and the same on a retry. Repeating a `generation_id` that already has a reply gets that reply back with `200` and `replayed: true`, charged nothing (`billed_cents: 0`; on a live key, `already_charged: true`). While the first request is still being drafted, the repeat answers `409 generation_in_progress` with `Retry-After`: retry with the same id. The same id sent for a different conversation is refused `409 generation_id_conflict`, never answered with the first conversation’s reply.\n- `POST /v1/developer/deliveries`: send your own `external_message_id`, and a repeated report is one message.\n\n## Teach SlyOS your business\nA reply is only as good as what SlyOS knows about the business it speaks for. Before the first one, hand it the material your answers must agree with — every reply is then given your rules and the passages nearest the message, and written in the voice SlyOS has learned for the account:\n- **Links and whole sites.** `POST /v1/developer/knowledge/sources` with `{\"url\": \"…\"}` reads one page — your pricing, your policies, a regulation your answers must respect. Add `\"crawl\": true` to read the whole site.\n- **Files.** The same call as `multipart/form-data`, with the file in a `file` field: PDF, Word, plain text, Markdown or CSV. It is read to the end.\n- **Rules.** `\"rules\": true` (a `rules=true` form field, for a file) marks a source replies must follow rather than draw on: terms, a returns policy, a regulation. Every reply carries the rules whole while they fit in one reply; past that, each reply gets the rules nearest its message, and the source says `rules_fit: false`.\n- **Who you are.** `PUT /v1/developer/knowledge/profile` says what the business is and how it wants to sound.\nSources are read in the background: `status` goes `queued`, `processing`, then `ready` — or `failed`, with `status_reason` saying why. A web page is read without running its scripts, so a page that builds its content with JavaScript (prices drawn from a script, a template filled in after it loads) can be missing what a visitor sees: the source is then `ready` with a `warning` naming those pages in `incomplete_pages` — add that text as a file, or as a single link to a page that shows it. Adding and changing them needs a **live** key carrying `brain:write` — sandbox and live replies share them, so a sandbox key may only read them; listing them, `brain:read`. `GET /v1/developer/knowledge/sources/{sourceId}/passages` shows exactly what was read.\nReplies use what you add, the conversation they answer and that customer’s earlier ones — never another customer’s, and never a sandbox conversation in a live reply. They also use everything SlyOS knows about the account owner: facts from their own conversations and the ones they told it, their past replies on their other channels, their documents, calendar and saved details, and their About you. The owner can turn that off on the Developer screen (`use_personal_context`); a key cannot change it. Off, a reply still uses the owner’s name, rules and writing style. Sources added with a key belong to its project unless the request sends `\"scope\": \"account\"`.\n**When sources disagree.** Each reply is given the passages nearest the message. The closest always goes in. The best passage of each other source about as relevant goes in ahead of the rest, in up to half of what a reply carries, so a long website cannot crowd out a short policy; after that, no one source takes more than half while another has something about as relevant. Where two disagree, the reply follows the higher one — rules, then uploaded files, then single links, then pages read from a whole site — and never contradicts the business’s own documents. That order settles disagreements only: a reply answers from the passage that answers the question, and says what another organisation’s page says as theirs.\nEvery reply then says what it relied on: `sources`, on the `POST /v1/developer/drafts` answer and on `GET /v1/developer/drafts/{draftId}`, lists the passages it was given — rules first — with the source, the page range or section, an excerpt of the passage’s own text, and a SHA-256 of the stored passage (with `carried_chars` when only its start fit).\n\n## Held replies\nA reply with `held_for_review: true` was held by SlyOS’s checks for a person to read first — money, health or dosing, a legal or data-protection matter, another sensitive subject, or something the reply gives out that the account has not sent before — and `hold_reasons` says which. Do not send it automatically. `null` means SlyOS can no longer say, and is held; so is a missing field. Every answer that carries a reply carries the hold: the first `POST /v1/developer/drafts` answer, a replay of it, `GET /v1/developer/drafts/{draftId}`, and the `draft.ready` webhook.\n\n## Errors\nEvery refusal has one shape, whatever the status: `{ \"code\": \"…\", \"message\": \"…\" }`. Branch on `code`; show `message` to a person. A `402 insufficient_funds` also carries `available_cents` and `needed_cents`; a `429 sandbox_daily_limit` carries `used_today` and `daily_limit`. Each operation lists its codes under the status it answers them with.\n- `400` — the request is malformed. Fix it; retrying the same request will not help.\n- `401` — the key is not valid. `403` — the key is valid but lacks the scope.\n- `402` — out of API credit. Not an outage: the message is stored and can be answered by hand.\n- `404` — no such object for this key.\n- `409` — a settled state, not a fault (for example, somebody asked whether they are talking to a person). Do not retry — except `generation_in_progress`, `not_stored`, `video_busy`, `video_no_takes_ready`, which the operations that answer them say are safe to retry with the same request.\n- `429` — over the rate limit, on an operation that lists `rate_limited`. Wait and retry.\n- `503` — SlyOS cannot reach a model provider. Retry with backoff; nothing was charged.\n\n## Limits\nEach operation states its rate limit in requests a minute; one that lists `429 rate_limited` refuses past it. A message body is at most 32,000 characters.\n\n## Webhooks\nSlyOS can post events to an endpoint the account owner sets on the Developer screen — one for sandbox and one for live, each with its own signing secret; a live endpoint must be HTTPS (see **webhooks**). Every delivery is signed in the `SlyOS-Signature` header; verify it before trusting the body. Delivery is at least once: an endpoint that does not answer `2xx` within ten seconds is tried again, up to 8 attempts over about 62 minutes, under the same event `id` — deduplicate on it. A redirect is not followed: it counts as a failed attempt.\n\n## Versioning\nThe version is in the path (`/v1`). Within a version, responses may gain fields: ignore fields you do not recognise.\n\nThis document is generated from the schemas the server validates with. The same content, readable: https://slyos.world/docs/api."
  },
  "servers": [
    {
      "url": "https://api.slyos.world",
      "description": "Production. Sandbox and live share this host; the key decides which one a call is."
    }
  ],
  "externalDocs": {
    "description": "Quickstart",
    "url": "https://slyos.world/docs/developer-api"
  },
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Messages"
    },
    {
      "name": "Drafts"
    },
    {
      "name": "Deliveries"
    },
    {
      "name": "Usage"
    },
    {
      "name": "Wallet"
    },
    {
      "name": "Channels"
    },
    {
      "name": "Knowledge"
    },
    {
      "name": "Videos"
    },
    {
      "name": "Video scripts"
    },
    {
      "name": "Video evaluations"
    },
    {
      "name": "Video usage"
    },
    {
      "name": "Webhooks"
    }
  ],
  "paths": {
    "/v1/developer/messages": {
      "post": {
        "operationId": "apiIngestMessage",
        "summary": "Send a message in",
        "description": "Take a message in from a custom channel. Free, always — including when the wallet is empty, so a ticket is never lost for want of credit.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `messages:write` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 1,200 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nRetry safely: the same `external_message_id` on the same channel is stored once.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry messages:write.\n- `not_found` — No such channel in this key’s environment.\n- `bad_request` — The payload is malformed — not JSON, no Content-Type: application/json, or a field of the wrong shape — or the body is over 32,000 characters.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `channel_disabled` — The channel is switched off in SlyOS. Nothing was stored.\n- `not_stored` — SlyOS could not store the message, and nothing was stored. Retry with the same external_message_id: it is stored once. If it keeps failing, keep the ticket on your side and tell us.",
        "tags": [
          "Messages"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiIngestMessageRequest"
              },
              "example": {
                "channel_id": "3f6c2b1e-8a4d-4c7f-9e2b-5d1a7c9e0f42",
                "external_thread_id": "ticket-48213",
                "external_message_id": "ticket-48213-comment-2",
                "body": "Do you ship to Canada, and how long does delivery take?",
                "sender_identifier": "dana@example.com",
                "sender_display_name": "Dana Whitfield",
                "sent_at": "2026-10-09T15:42:07Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This `external_message_id` was already stored on this channel; nothing new was written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIngestMessageResponse"
                },
                "example": {
                  "message_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18",
                  "thread_id": "c4a7e2d9-1b6f-4e83-9a50-7d2c8f1e3b65",
                  "card_id": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14",
                  "duplicate": true,
                  "needs_reply": true,
                  "bulk": false
                }
              }
            }
          },
          "201": {
            "description": "The message is new and was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIngestMessageResponse"
                },
                "example": {
                  "message_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18",
                  "thread_id": "c4a7e2d9-1b6f-4e83-9a50-7d2c8f1e3b65",
                  "card_id": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14",
                  "duplicate": false,
                  "needs_reply": true,
                  "bulk": false
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The payload is malformed — not JSON, no Content-Type: application/json, or a field of the wrong shape — or the body is over 32,000 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The payload is malformed — not JSON, no Content-Type: application/json, or a field of the wrong shape — or the body is over 32,000 characters."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry messages:write.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry messages:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such channel in this key’s environment.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such channel in this key’s environment."
                }
              }
            }
          },
          "409": {
            "description": "`channel_disabled` — The channel is switched off in SlyOS. Nothing was stored.\n\n`not_stored` — SlyOS could not store the message, and nothing was stored. Retry with the same external_message_id: it is stored once. If it keeps failing, keep the ticket on your side and tell us.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "channel_disabled",
                            "not_stored"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "channel_disabled",
                  "message": "The channel is switched off in SlyOS. Nothing was stored."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "messages:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 1200,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/drafts": {
      "post": {
        "operationId": "apiGenerateDraft",
        "summary": "Ask for a reply",
        "description": "Ask SlyOS to write a reply, using the account’s Brain, voice and history. The one billable call: 10¢ per successful response, charged once per generation_id.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `drafts:generate` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 300 requests a minute; past it, `429 rate_limited`\n- **Cost:** 10¢ per AI response on a live key, charged once per `generation_id`; free in the sandbox\n\nThe reply is written by the account’s own Brain — what the business has taught SlyOS, the voice it has learned for the channel, and the conversation so far — through the same pipeline as the SlyOS inbox.\n\nThe only call that costs anything, and only on a live key. A request that does not produce a reply — refused, failed, or not needed — is not charged.\n\nCheck `held_for_review` before you send — on a replay too, which carries the hold the reply was written with. Send automatically only on `false`; `true` or `null` goes to a person, never straight to your customer, with `hold_reasons` saying why.\n\nA replay (`already_charged: true`) is a reply SlyOS already gave you once. If your first request may have sent it, do not send it again: check your own record of what went out.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry drafts:generate.\n- `bad_request` — The payload is malformed — a generation_id shorter than 8 or longer than 128 characters, an id that is not a UUID — or neither card_id nor message_id was given, or they name two different conversations.\n- `not_found` — No such message or card in this key’s environment. A sandbox key cannot reach a live card, and neither can reach another channel’s.\n- `no_card` — That message needs no reply, so there is nothing to draft.\n- `channel_paused` — The channel is paused on the Developer screen, so nothing is drafted on it. Nothing was charged.\n- `insufficient_funds` — No API credit remains. The message is still stored and visible, and can be answered by hand. Nothing was charged.\n- `generation_in_progress` — Another request with this generation_id is still being drafted. Retry after Retry-After with the same id to get that draft, charged once.\n- `generation_id_conflict` — That generation_id is already taken on this account. Nothing was charged.\n- `draft_gone` — The reply this generation_id produced was deleted with its conversation. Use a new generation_id.\n- `paused_identity` — Someone asked whether they are talking to a person; drafting is paused until the account owner clears it. Nothing was charged.\n- `paused_allowance` — Sandbox only: the plan’s AI allowance is used up, and sandbox drafts are drawn from the plan. Live drafts are paid from the wallet and never refused for this. Nothing was charged.\n- `filed_away` — The account owner archived or filed the conversation. Do not retry. Nothing was charged.\n- `no_reply_needed` — The model judged nobody is waiting for a reply (an automated message, say), so no draft was written. Nothing was charged.\n- `refused` — No draft could be produced for that card: its conversation or its channel could not be used. A card_id that does not exist is a 404 `not_found`, not this. Nothing was charged.\n- `ai_off` — The account owner has not allowed SlyOS to send their data to AI providers. Nothing was sent or charged. Do not retry until they allow it in the app.\n- `partner_ai_paused` — The account’s partner pays for its AI with the partner’s own key, and that key is not set or the AI provider refused it. Nothing was sent on any other key, and nothing was charged. Do not retry until the partner has fixed it.\n- `outage` — No model provider is reachable. Retry after Retry-After with the same generation_id. Nothing was charged.\n- `try_again` — The draft could not be charged for in time, so it was not. Retry with the same generation_id.\n- `drafting_unavailable` — Drafting is not configured on this deployment.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `sandbox_daily_limit` — Sandbox only: this account has had 50 sandbox drafts in the last 24 hours. Retry after Retry-After, or use a live key. A replay of a draft already written is not counted. Nothing was charged.",
        "tags": [
          "Drafts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiGenerateDraftRequest"
              },
              "example": {
                "generation_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18:reply-1",
                "message_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A live key sent a `generation_id` that was already paid for: the same reply again, with the hold it was written with, charged nothing. It may already have been sent — do not send it again automatically.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiGenerateDraftResponse"
                },
                "example": {
                  "draft_id": "e1f7a3c5-9d2b-4e68-b0a4-6c3e8f1d2a97",
                  "card_id": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14",
                  "generation": 1,
                  "body": "Hi Dana — yes, we ship to Canada. Orders usually arrive within 5–8 business days, and you’ll get a tracking link by email as soon as yours leaves our warehouse.",
                  "held_for_review": false,
                  "hold_reasons": [],
                  "hold_detail": null,
                  "billed_cents": 0,
                  "already_charged": true,
                  "originally_billed_cents": 10,
                  "price_version": "2026-09-01",
                  "environment": "live",
                  "replayed": true,
                  "generation_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18:reply-1",
                  "sources": [
                    {
                      "source_id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                      "passage_id": "2f9e4c1a-7b3d-4e8f-a5c2-6d1b9e3f7a40",
                      "title": "Example Co — website",
                      "url": "https://www.example.com/shipping",
                      "page": null,
                      "page_end": null,
                      "section": "Shipping",
                      "rule": false,
                      "rank": 1,
                      "score": 0.83,
                      "excerpt": "We ship to the US and Canada. Orders leave our warehouse within two business days.",
                      "text_sha256": "3b7e1f9a2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e0f1a3b5c7d9e1f2a4b6c8d0e2f",
                      "carried_chars": null
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "A reply was written for this `generation_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiGenerateDraftResponse"
                },
                "example": {
                  "draft_id": "e1f7a3c5-9d2b-4e68-b0a4-6c3e8f1d2a97",
                  "card_id": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14",
                  "generation": 1,
                  "body": "Hi Dana — yes, we ship to Canada. Orders usually arrive within 5–8 business days, and you’ll get a tracking link by email as soon as yours leaves our warehouse.",
                  "held_for_review": false,
                  "hold_reasons": [],
                  "hold_detail": null,
                  "billed_cents": 10,
                  "already_charged": false,
                  "price_version": "2026-09-01",
                  "environment": "live",
                  "replayed": false,
                  "generation_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18:reply-1",
                  "sources": [
                    {
                      "source_id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                      "passage_id": "2f9e4c1a-7b3d-4e8f-a5c2-6d1b9e3f7a40",
                      "title": "Example Co — website",
                      "url": "https://www.example.com/shipping",
                      "page": null,
                      "page_end": null,
                      "section": "Shipping",
                      "rule": false,
                      "rank": 1,
                      "score": 0.83,
                      "excerpt": "We ship to the US and Canada. Orders leave our warehouse within two business days.",
                      "text_sha256": "3b7e1f9a2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e0f1a3b5c7d9e1f2a4b6c8d0e2f",
                      "carried_chars": null
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The payload is malformed — a generation_id shorter than 8 or longer than 128 characters, an id that is not a UUID — or neither card_id nor message_id was given, or they name two different conversations.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The payload is malformed — a generation_id shorter than 8 or longer than 128 characters, an id that is not a UUID — or neither card_id nor message_id was given, or they name two different conversations."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`insufficient_funds` — No API credit remains. The message is still stored and visible, and can be answered by hand. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiInsufficientFunds"
                },
                "example": {
                  "code": "insufficient_funds",
                  "message": "This account has no API credit left. The message is stored and visible in SlyOS, and can still be answered by hand.",
                  "available_cents": 9,
                  "needed_cents": 10
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry drafts:generate.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry drafts:generate."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such message or card in this key’s environment. A sandbox key cannot reach a live card, and neither can reach another channel’s.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such message or card in this key’s environment. A sandbox key cannot reach a live card, and neither can reach another channel’s."
                }
              }
            }
          },
          "409": {
            "description": "`no_card` — That message needs no reply, so there is nothing to draft.\n\n`channel_paused` — The channel is paused on the Developer screen, so nothing is drafted on it. Nothing was charged.\n\n`generation_in_progress` — Another request with this generation_id is still being drafted. Retry after Retry-After with the same id to get that draft, charged once.\n\n`generation_id_conflict` — That generation_id is already taken on this account. Nothing was charged.\n\n`draft_gone` — The reply this generation_id produced was deleted with its conversation. Use a new generation_id.\n\n`paused_identity` — Someone asked whether they are talking to a person; drafting is paused until the account owner clears it. Nothing was charged.\n\n`paused_allowance` — Sandbox only: the plan’s AI allowance is used up, and sandbox drafts are drawn from the plan. Live drafts are paid from the wallet and never refused for this. Nothing was charged.\n\n`filed_away` — The account owner archived or filed the conversation. Do not retry. Nothing was charged.\n\n`no_reply_needed` — The model judged nobody is waiting for a reply (an automated message, say), so no draft was written. Nothing was charged.\n\n`refused` — No draft could be produced for that card: its conversation or its channel could not be used. A card_id that does not exist is a 404 `not_found`, not this. Nothing was charged.\n\n`ai_off` — The account owner has not allowed SlyOS to send their data to AI providers. Nothing was sent or charged. Do not retry until they allow it in the app.\n\n`partner_ai_paused` — The account’s partner pays for its AI with the partner’s own key, and that key is not set or the AI provider refused it. Nothing was sent on any other key, and nothing was charged. Do not retry until the partner has fixed it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "no_card",
                            "channel_paused",
                            "generation_in_progress",
                            "generation_id_conflict",
                            "draft_gone",
                            "paused_identity",
                            "paused_allowance",
                            "filed_away",
                            "no_reply_needed",
                            "refused",
                            "ai_off",
                            "partner_ai_paused"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "no_card",
                  "message": "That message needs no reply, so there is nothing to draft."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n\n`sandbox_daily_limit` — Sandbox only: this account has had 50 sandbox drafts in the last 24 hours. Retry after Retry-After, or use a live key. A replay of a draft already written is not counted. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ApiSandboxDailyLimit"
                    },
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ApiRefusal"
                        },
                        {
                          "properties": {
                            "code": {
                              "enum": [
                                "rate_limited"
                              ]
                            }
                          }
                        }
                      ]
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`outage` — No model provider is reachable. Retry after Retry-After with the same generation_id. Nothing was charged.\n\n`try_again` — The draft could not be charged for in time, so it was not. Retry with the same generation_id.\n\n`drafting_unavailable` — Drafting is not configured on this deployment.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "outage",
                            "try_again",
                            "drafting_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "outage",
                  "message": "No model provider is reachable. Retry after Retry-After with the same generation_id. Nothing was charged."
                }
              }
            }
          }
        },
        "x-slyos-scope": "drafts:generate",
        "x-slyos-billable": true,
        "x-slyos-rate-limit-per-minute": 300,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/drafts/{draftId}": {
      "get": {
        "operationId": "apiReadDraft",
        "summary": "Read a reply back",
        "description": "Read a draft back. Free.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `drafts:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 600 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry drafts:read.\n- `bad_request` — The draft id is not a UUID.\n- `not_found` — No such draft in this key’s environment.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
        "tags": [
          "Drafts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "draftId",
            "in": "path",
            "required": true,
            "description": "The `draft_id` to read, from `POST /v1/developer/drafts` or `draft.ready`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The draft exists on this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiReadDraftResponse"
                },
                "example": {
                  "draft_id": "e1f7a3c5-9d2b-4e68-b0a4-6c3e8f1d2a97",
                  "card_id": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14",
                  "body": "Hi Dana — yes, we ship to Canada. Orders usually arrive within 5–8 business days, and you’ll get a tracking link by email as soon as yours leaves our warehouse.",
                  "generation": 1,
                  "model": "claude-opus-5-5",
                  "created_at": "2026-10-09T15:42:11.000Z",
                  "held_for_review": false,
                  "hold_reasons": [],
                  "hold_detail": null,
                  "superseded": false,
                  "sources": [
                    {
                      "source_id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                      "passage_id": "2f9e4c1a-7b3d-4e8f-a5c2-6d1b9e3f7a40",
                      "title": "Example Co — website",
                      "url": "https://www.example.com/shipping",
                      "page": null,
                      "page_end": null,
                      "section": "Shipping",
                      "rule": false,
                      "rank": 1,
                      "score": 0.83,
                      "excerpt": "We ship to the US and Canada. Orders leave our warehouse within two business days.",
                      "text_sha256": "3b7e1f9a2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e0f1a3b5c7d9e1f2a4b6c8d0e2f",
                      "carried_chars": null
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The draft id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The draft id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry drafts:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry drafts:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such draft in this key’s environment.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such draft in this key’s environment."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "drafts:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 600,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/deliveries": {
      "post": {
        "operationId": "apiAcknowledgeDelivery",
        "summary": "Report what happened to a send",
        "description": "Tell SlyOS what happened to the reply you sent through your own system. Recorded, and — given draft_id or body — written into the conversation so every SlyOS surface shows it. Free — we do not charge a customer for reporting their own send.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `deliveries:write` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 1,200 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nSend `draft_id` when you sent SlyOS’s draft as it was, or `body` with the text you actually sent. The reply then shows in the conversation on every SlyOS surface and its card closes; without either, the report is recorded and no text is invented for it.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry deliveries:write.\n- `bad_request` — The payload is malformed — message_id or draft_id is not a UUID, a body is empty or too long — or the status is not delivered, read or failed.\n- `not_found` — No such message in this key’s environment, or the draft named is not on that message’s conversation.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiAcknowledgeDeliveryRequest"
              },
              "example": {
                "message_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18",
                "status": "delivered",
                "draft_id": "e1f7a3c5-9d2b-4e68-b0a4-6c3e8f1d2a97",
                "occurred_at": "2026-10-09T15:44:02Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The report was recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAcknowledgeDeliveryResponse"
                },
                "example": {
                  "delivery_id": "4d7b1e9f-2a6c-4f35-b8e0-9c3a5f1d7e42",
                  "message_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18",
                  "status": "delivered",
                  "recorded": true,
                  "outbound_message_id": "b3e9c7a1-5d2f-4e86-a0c4-7f1b3d9e5a28",
                  "draft_id": "e1f7a3c5-9d2b-4e68-b0a4-6c3e8f1d2a97",
                  "card_closed": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The payload is malformed — message_id or draft_id is not a UUID, a body is empty or too long — or the status is not delivered, read or failed.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The payload is malformed — message_id or draft_id is not a UUID, a body is empty or too long — or the status is not delivered, read or failed."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry deliveries:write.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry deliveries:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such message in this key’s environment, or the draft named is not on that message’s conversation.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such message in this key’s environment, or the draft named is not on that message’s conversation."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "deliveries:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 1200,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/usage": {
      "get": {
        "operationId": "apiUsage",
        "summary": "Read usage",
        "description": "Responses written for THIS key’s environment and project, and what they cost, over the last 30 days. Free. A sandbox key sees sandbox drafts at nought cents, never the account’s live spend.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `usage:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry usage:read.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
        "tags": [
          "Usage"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Always, for a valid key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiUsageResponse"
                },
                "example": {
                  "environment": "live",
                  "window_days": 30,
                  "responses": 412,
                  "charged_cents": 4120,
                  "price": {
                    "version": "2026-09-01",
                    "cents_per_response": 10
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry usage:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry usage:read."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "usage:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/wallet": {
      "get": {
        "operationId": "apiWallet",
        "summary": "Check the balance",
        "description": "Balance, reserved, and how many responses remain — so an adapter can degrade before it is refused rather than after. A live key reads the wallet; a sandbox key reads no money at all (zeros) and how many of today’s free sandbox drafts are left.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `usage:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 240 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nCall it before a burst: an adapter that checks first can queue or hand over to a person before it is refused.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry usage:read.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
        "tags": [
          "Wallet"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Always, for a valid key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiWalletResponse"
                },
                "example": {
                  "environment": "live",
                  "balance_cents": 1870,
                  "reserved_cents": 10,
                  "available_cents": 1860,
                  "responses_remaining": 186
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry usage:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry usage:read."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "usage:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 240,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/channels": {
      "post": {
        "operationId": "apiCreateChannel",
        "summary": "Create a channel",
        "description": "Create a custom channel. Its environment is the KEY’s — a live key makes a live channel — so a sandbox channel can never end up on live traffic by typo.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `channels:manage` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 60 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nThe environment is the key’s: a live key makes a live channel and a sandbox key a sandbox one. There is no field to say otherwise, so a sandbox channel cannot end up on live traffic by typo.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry channels:manage.\n- `bad_request` — The payload is malformed: no name, a name over 120 characters, or a capability outside the published set.\n- `no_slots` — Every channel slot on this plan is in use. Upgrade, or disconnect a channel first. Sandbox channels are free and do not count.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
        "tags": [
          "Channels"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiCreateChannelRequest"
              },
              "example": {
                "name": "Support desk",
                "system": "Zendesk",
                "capabilities": [
                  "receive",
                  "send",
                  "threading"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The channel was created, in the key’s environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiChannel"
                },
                "example": {
                  "id": "3f6c2b1e-8a4d-4c7f-9e2b-5d1a7c9e0f42",
                  "name": "Support desk",
                  "system": "Zendesk",
                  "environment": "sandbox",
                  "capabilities": [
                    "receive",
                    "send",
                    "threading"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The payload is malformed: no name, a name over 120 characters, or a capability outside the published set.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The payload is malformed: no name, a name over 120 characters, or a capability outside the published set."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`no_slots` — Every channel slot on this plan is in use. Upgrade, or disconnect a channel first. Sandbox channels are free and do not count.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "no_slots"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "no_slots",
                  "message": "Every channel slot on this plan is in use. Upgrade, or disconnect a channel first. Sandbox channels are free and do not count."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry channels:manage.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry channels:manage."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "channels:manage",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 60,
        "x-slyos-rate-limit-enforced": true
      },
      "get": {
        "operationId": "apiListChannels",
        "summary": "List channels",
        "description": "The custom channels this key can reach, with their declared capabilities. Free.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `channels:manage` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry channels:manage.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
        "tags": [
          "Channels"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Always, for a valid key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiListChannelsResponse"
                },
                "example": {
                  "channels": [
                    {
                      "id": "3f6c2b1e-8a4d-4c7f-9e2b-5d1a7c9e0f42",
                      "name": "Support desk",
                      "system": "Zendesk",
                      "environment": "sandbox",
                      "capabilities": [
                        "receive",
                        "send",
                        "threading"
                      ],
                      "paused": false,
                      "auto_draft": false,
                      "last_inbound_at": "2026-10-09T15:42:08.000Z",
                      "last_outbound_at": null
                    }
                  ],
                  "known_capabilities": [
                    "receive",
                    "send",
                    "attachments",
                    "read_receipts",
                    "mark_read",
                    "delivery_receipts",
                    "reactions",
                    "typing",
                    "threading",
                    "groups"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry channels:manage.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry channels:manage."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "channels:manage",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/knowledge/sources": {
      "post": {
        "operationId": "apiAddKnowledge",
        "summary": "Add a link, a site or a file",
        "description": "Add a source: JSON {url, crawl?, rules?, title?, scope?} for a page or a whole site, or multipart with a `file` (PDF, Word, text, Markdown, CSV; up to 25 MB). Answers 202; the source is read in the background. Needs a live key.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:write` scope.\n- **Entitlement:** PDF pages a model reads, per account per 30 days: 100, or 2,000 on a paid plan or with money in the API wallet\n- **Rate limit:** 60 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nA link or a site is JSON: `{\"url\": \"…\"}`, with `\"crawl\": true` to read the whole site. A file is `multipart/form-data` with the file in a `file` field.\n\nMark terms, policies and regulations `rules`: replies must follow a rule, and draw on everything else as reference.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:write.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `sandbox_read_only` — A sandbox key can read knowledge but not change it.\n- `bad_request` — No url and no file, or an address SlyOS will not read.\n- `unsupported_type` — Not a file type SlyOS reads (PDF, Word .docx, plain text, Markdown, CSV).\n- `too_large` — The file is over 25 MB.\n- `too_many_sources` — The account already holds the most knowledge sources it may.\n- `knowledge_full` — The account already holds the most knowledge text it may.\n- `busy` — Too many sources are being read at once. Add more when they finish.\n- `uploads_busy` — Two files are already uploading for this account. Send the next after.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiAddKnowledgeLinkRequest"
              },
              "example": {
                "url": "https://www.example.com/",
                "crawl": true
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ApiAddKnowledgeFileRequest"
              },
              "example": {
                "file": "@returns-policy.pdf",
                "rules": "true",
                "title": "Returns policy"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "That address, or that exact file, is already a source: this is it, and nothing new was added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAddKnowledgeResponse"
                },
                "example": {
                  "id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                  "kind": "site",
                  "url": "https://www.example.com/",
                  "filename": null,
                  "title": "Example Co — website",
                  "rules": false,
                  "scope": "project",
                  "status": "ready",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 24,
                  "passages": 187,
                  "characters": 96412,
                  "size_bytes": null,
                  "content_sha256": "4f2a9c0e7b1d3e5f6a8c9b0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f",
                  "file_sha256": null,
                  "used_in_replies": 38,
                  "rules_fit": null,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:21:37.000Z",
                  "fetched_at": "2026-10-09T15:21:37.000Z",
                  "duplicate": true
                }
              }
            }
          },
          "202": {
            "description": "Taken in. It is read in the background: poll `GET /v1/developer/knowledge/sources/{sourceId}` until `status` is `ready`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAddKnowledgeResponse"
                },
                "example": {
                  "id": "0e7c2a9d-5f1b-4d38-a6e4-9b3c1f7d2a85",
                  "kind": "file",
                  "url": null,
                  "filename": "returns-policy.pdf",
                  "title": "Returns policy",
                  "rules": true,
                  "scope": "project",
                  "status": "queued",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 0,
                  "passages": 0,
                  "characters": 0,
                  "size_bytes": 284311,
                  "content_sha256": null,
                  "file_sha256": "9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c",
                  "used_in_replies": 0,
                  "rules_fit": true,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:20:04.000Z",
                  "fetched_at": null,
                  "duplicate": false
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — No url and no file, or an address SlyOS will not read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "No url and no file, or an address SlyOS will not read."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:write.\n\n`sandbox_read_only` — A sandbox key can read knowledge but not change it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "sandbox_read_only"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:write."
                }
              }
            }
          },
          "409": {
            "description": "`too_many_sources` — The account already holds the most knowledge sources it may.\n\n`knowledge_full` — The account already holds the most knowledge text it may.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_many_sources",
                            "knowledge_full"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_many_sources",
                  "message": "The account already holds the most knowledge sources it may."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The file is over 25 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The file is over 25 MB."
                }
              }
            }
          },
          "422": {
            "description": "`unsupported_type` — Not a file type SlyOS reads (PDF, Word .docx, plain text, Markdown, CSV).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unsupported_type"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unsupported_type",
                  "message": "Not a file type SlyOS reads (PDF, Word .docx, plain text, Markdown, CSV)."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n\n`busy` — Too many sources are being read at once. Add more when they finish.\n\n`uploads_busy` — Two files are already uploading for this account. Send the next after.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited",
                            "busy",
                            "uploads_busy"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 60,
        "x-slyos-rate-limit-enforced": true
      },
      "get": {
        "operationId": "apiListKnowledge",
        "summary": "List knowledge sources",
        "description": "The sources this key’s replies use — its project’s and the account’s — with status. Free.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 240 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:read.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Always, for a valid key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiListKnowledgeResponse"
                },
                "example": {
                  "sources": [
                    {
                      "id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                      "kind": "site",
                      "url": "https://www.example.com/",
                      "filename": null,
                      "title": "Example Co — website",
                      "rules": false,
                      "scope": "project",
                      "status": "ready",
                      "status_reason": null,
                      "warning": null,
                      "incomplete_pages": [],
                      "pages": 24,
                      "passages": 187,
                      "characters": 96412,
                      "size_bytes": null,
                      "content_sha256": "4f2a9c0e7b1d3e5f6a8c9b0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f",
                      "file_sha256": null,
                      "used_in_replies": 38,
                      "rules_fit": null,
                      "rules_note": null,
                      "created_at": "2026-10-09T15:20:04.000Z",
                      "updated_at": "2026-10-09T15:21:37.000Z",
                      "fetched_at": "2026-10-09T15:21:37.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:read."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 240,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/knowledge/sources/{sourceId}": {
      "get": {
        "operationId": "apiReadKnowledge",
        "summary": "Read a knowledge source",
        "description": "One source: status and why, pages and passages read, and how many replies relied on it. Free.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 240 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:read.\n- `bad_request` — The source id is not a UUID.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `not_found` — No such source for this key.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "description": "A knowledge source’s `id`, from adding it or listing them.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The source exists for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKnowledgeSource"
                },
                "example": {
                  "id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                  "kind": "site",
                  "url": "https://www.example.com/",
                  "filename": null,
                  "title": "Example Co — website",
                  "rules": false,
                  "scope": "project",
                  "status": "ready",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 24,
                  "passages": 187,
                  "characters": 96412,
                  "size_bytes": null,
                  "content_sha256": "4f2a9c0e7b1d3e5f6a8c9b0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f",
                  "file_sha256": null,
                  "used_in_replies": 38,
                  "rules_fit": null,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:21:37.000Z",
                  "fetched_at": "2026-10-09T15:21:37.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The source id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The source id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such source for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such source for this key."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 240,
        "x-slyos-rate-limit-enforced": true
      },
      "patch": {
        "operationId": "apiUpdateKnowledge",
        "summary": "Mark a source as rules, or rename it",
        "description": "Mark a source as binding rules or as reference material, or rename it.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:write` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 60 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:write.\n- `bad_request` — The source id is not a UUID, or the body is malformed: nothing to change (send rules or title), or a title over 300 characters.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `sandbox_read_only` — A sandbox key can read knowledge but not change it.\n- `not_found` — No such source for this key.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "description": "A knowledge source’s `id`, from adding it or listing them.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiUpdateKnowledgeRequest"
              },
              "example": {
                "rules": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Changed. The next reply uses it as changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKnowledgeSource"
                },
                "example": {
                  "id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                  "kind": "site",
                  "url": "https://www.example.com/",
                  "filename": null,
                  "title": "Example Co — website",
                  "rules": true,
                  "scope": "project",
                  "status": "ready",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 24,
                  "passages": 187,
                  "characters": 96412,
                  "size_bytes": null,
                  "content_sha256": "4f2a9c0e7b1d3e5f6a8c9b0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f",
                  "file_sha256": null,
                  "used_in_replies": 38,
                  "rules_fit": true,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:21:37.000Z",
                  "fetched_at": "2026-10-09T15:21:37.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The source id is not a UUID, or the body is malformed: nothing to change (send rules or title), or a title over 300 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The source id is not a UUID, or the body is malformed: nothing to change (send rules or title), or a title over 300 characters."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:write.\n\n`sandbox_read_only` — A sandbox key can read knowledge but not change it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "sandbox_read_only"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such source for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such source for this key."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 60,
        "x-slyos-rate-limit-enforced": true
      },
      "delete": {
        "operationId": "apiDeleteKnowledge",
        "summary": "Delete a source",
        "description": "Delete a source and every passage read from it. Replies stop using it at once.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:write` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 60 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:write.\n- `bad_request` — The source id is not a UUID.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `sandbox_read_only` — A sandbox key can read knowledge but not change it.\n- `not_found` — No such source for this key.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "description": "A knowledge source’s `id`, from adding it or listing them.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted, with every passage read from it. No reply uses it from now on.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiDeleteKnowledgeResponse"
                },
                "example": {
                  "id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                  "deleted": true
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The source id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The source id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:write.\n\n`sandbox_read_only` — A sandbox key can read knowledge but not change it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "sandbox_read_only"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such source for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such source for this key."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 60,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/knowledge/sources/{sourceId}/passages": {
      "get": {
        "operationId": "apiKnowledgePassages",
        "summary": "Read what was taken from a source",
        "description": "The passages read from a source, in order, with page, section and URL: exactly what replies retrieve from. Free.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nExactly the passages replies retrieve from — check here what SlyOS read.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:read.\n- `bad_request` — The source id is not a UUID, or limit is not 1 to 200, or after is not a whole number from -1.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `not_found` — No such source for this key.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "description": "A knowledge source’s `id`, from adding it or listing them.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many passages to return.",
            "schema": {
              "default": 50,
              "description": "How many passages to return.",
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Return passages after this `ordinal`: the previous page’s `next_after`.",
            "schema": {
              "default": -1,
              "description": "Return passages after this `ordinal`: the previous page’s `next_after`.",
              "type": "integer",
              "minimum": -1,
              "maximum": 9007199254740991
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The source exists for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKnowledgePassagesResponse"
                },
                "example": {
                  "source_id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                  "passages": [
                    {
                      "id": "2f9e4c1a-7b3d-4e8f-a5c2-6d1b9e3f7a40",
                      "ordinal": 0,
                      "page": null,
                      "page_end": null,
                      "section": "Shipping",
                      "url": "https://www.example.com/shipping",
                      "text": "We ship to the US and Canada. Orders leave our warehouse within two business days."
                    }
                  ],
                  "next_after": null
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The source id is not a UUID, or limit is not 1 to 200, or after is not a whole number from -1.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The source id is not a UUID, or limit is not 1 to 200, or after is not a whole number from -1."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such source for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such source for this key."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/knowledge/sources/{sourceId}/refresh": {
      "post": {
        "operationId": "apiRefreshKnowledge",
        "summary": "Read a link or a site again",
        "description": "Read a link or a site again. The copy already read keeps answering until the new one is ready.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:write` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 30 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nRefresh after your site or a regulation page changes; replies use the new text once it is `ready`.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:write.\n- `bad_request` — The source id is not a UUID.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `sandbox_read_only` — A sandbox key can read knowledge but not change it.\n- `not_found` — No such source for this key.\n- `cannot_refresh` — A file is read once and not kept. Upload the new version instead.\n- `busy` — That source is being read right now.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "description": "A knowledge source’s `id`, from adding it or listing them.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Queued. The copy already read keeps answering until the new one is ready.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKnowledgeSource"
                },
                "example": {
                  "id": "6b1d4f8a-3c2e-4a9f-b7d0-1e5c8a2f4d63",
                  "kind": "site",
                  "url": "https://www.example.com/",
                  "filename": null,
                  "title": "Example Co — website",
                  "rules": false,
                  "scope": "project",
                  "status": "queued",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 24,
                  "passages": 187,
                  "characters": 96412,
                  "size_bytes": null,
                  "content_sha256": "4f2a9c0e7b1d3e5f6a8c9b0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f",
                  "file_sha256": null,
                  "used_in_replies": 38,
                  "rules_fit": null,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:21:37.000Z",
                  "fetched_at": "2026-10-09T15:21:37.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The source id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The source id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:write.\n\n`sandbox_read_only` — A sandbox key can read knowledge but not change it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "sandbox_read_only"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such source for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such source for this key."
                }
              }
            }
          },
          "409": {
            "description": "`cannot_refresh` — A file is read once and not kept. Upload the new version instead.\n\n`busy` — That source is being read right now.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "cannot_refresh",
                            "busy"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "cannot_refresh",
                  "message": "A file is read once and not kept. Upload the new version instead."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 30,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/knowledge/profile": {
      "get": {
        "operationId": "apiBusinessProfile",
        "summary": "Read the business profile",
        "description": "The business profile this key’s replies use: name, description, tone notes. Free.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:read` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:read.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Always, for a valid key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiBusinessProfile"
                },
                "example": {
                  "business_name": "Example Co",
                  "description": "We make and sell refillable kitchen goods online, shipping to the US and Canada. We do not take phone orders.",
                  "tone_notes": "Warm and brief. Sign off as “The Example Co team”.",
                  "use_personal_context": true,
                  "updated_at": "2026-10-09T15:18:52.000Z"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:read."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      },
      "put": {
        "operationId": "apiSaveBusinessProfile",
        "summary": "Say who the business is and how it sounds",
        "description": "Set what the business is and how it wants to sound. Used in every API reply of this key’s project.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `brain:write` scope.\n- **Entitlement:** none — every tier may call it\n- **Rate limit:** 30 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nA field you leave out is left as it was; `null` clears it. Whether replies use everything SlyOS knows about the owner (`use_personal_context`) is the owner’s switch, on the Developer screen: a key may send back the value `GET` answered, and a key that tries to change it is refused.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry brain:write.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.\n- `sandbox_read_only` — A sandbox key can read knowledge but not change it.\n- `bad_request` — The body is malformed — a field this route does not take, or one longer than it may be — or it tries to change use_personal_context, which only the account owner can, on the Developer screen.",
        "tags": [
          "Knowledge"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiSaveBusinessProfileRequest"
              },
              "example": {
                "business_name": "Example Co",
                "description": "We make and sell refillable kitchen goods online, shipping to the US and Canada. We do not take phone orders.",
                "tone_notes": "Warm and brief. Sign off as “The Example Co team”."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved. Every reply asked for with this key’s project uses it from now on.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiBusinessProfile"
                },
                "example": {
                  "business_name": "Example Co",
                  "description": "We make and sell refillable kitchen goods online, shipping to the US and Canada. We do not take phone orders.",
                  "tone_notes": "Warm and brief. Sign off as “The Example Co team”.",
                  "use_personal_context": true,
                  "updated_at": "2026-10-09T15:18:52.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The body is malformed — a field this route does not take, or one longer than it may be — or it tries to change use_personal_context, which only the account owner can, on the Developer screen.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The body is malformed — a field this route does not take, or one longer than it may be — or it tries to change use_personal_context, which only the account owner can, on the Developer screen."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry brain:write.\n\n`sandbox_read_only` — A sandbox key can read knowledge but not change it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "sandbox_read_only"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry brain:write."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-scope": "brain:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 30,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/knowledge/uploads/{token}": {
      "post": {
        "operationId": "apiKnowledgeUpload",
        "summary": "Upload a file to a one-time address",
        "description": "Where the account’s own screen uploads a file directly (multipart `file`), with a one-time token from developerKnowledgeUploadUrl in place of a key.\n\n- **Auth:** no API key — this step is authorised by what its URL carries.\n- **Entitlement:** PDF pages a model reads, per account per 30 days: 100, or 2,000 on a paid plan or with money in the API wallet\n- **Rate limit:** 30 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nFor the Developer screen’s own uploads: the address carries a one-time token in place of a key. From your server, use `POST /v1/developer/knowledge/sources`.\n\n**Errors**\n- `not_found` — The upload address is unknown, used, or expired. Ask for a new one.\n- `rate_limited` — Over this route’s per-minute limit, counted per account. Wait a minute.\n- `too_many_sources` — The account already holds the most knowledge sources it may.\n- `knowledge_full` — The account already holds the most knowledge text it may.\n- `busy` — Too many sources are being read at once. Add more when they finish.\n- `uploads_busy` — Two files are already uploading for this account. Send the next after.\n- `bad_request` — No file in the upload, or the upload address is malformed.\n- `unsupported_type` — Not a file type SlyOS reads (PDF, Word .docx, plain text, Markdown, CSV).\n- `too_large` — The file is over 25 MB.",
        "tags": [
          "Knowledge"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "description": "The one-time token in the upload address SlyOS gave the Developer screen.",
            "schema": {
              "type": "string",
              "minLength": 20,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ApiKnowledgeUploadRequest"
              },
              "example": {
                "file": "@returns-policy.pdf",
                "title": "Returns policy"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "That exact file is already a source: this is it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAddKnowledgeResponse"
                },
                "example": {
                  "id": "0e7c2a9d-5f1b-4d38-a6e4-9b3c1f7d2a85",
                  "kind": "file",
                  "url": null,
                  "filename": "returns-policy.pdf",
                  "title": "Returns policy",
                  "rules": true,
                  "scope": "project",
                  "status": "ready",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 0,
                  "passages": 0,
                  "characters": 0,
                  "size_bytes": 284311,
                  "content_sha256": null,
                  "file_sha256": "9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c",
                  "used_in_replies": 0,
                  "rules_fit": true,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:20:04.000Z",
                  "fetched_at": null,
                  "duplicate": true
                }
              }
            }
          },
          "202": {
            "description": "Taken in, and read in the background.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAddKnowledgeResponse"
                },
                "example": {
                  "id": "0e7c2a9d-5f1b-4d38-a6e4-9b3c1f7d2a85",
                  "kind": "file",
                  "url": null,
                  "filename": "returns-policy.pdf",
                  "title": "Returns policy",
                  "rules": true,
                  "scope": "project",
                  "status": "queued",
                  "status_reason": null,
                  "warning": null,
                  "incomplete_pages": [],
                  "pages": 0,
                  "passages": 0,
                  "characters": 0,
                  "size_bytes": 284311,
                  "content_sha256": null,
                  "file_sha256": "9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c",
                  "used_in_replies": 0,
                  "rules_fit": true,
                  "rules_note": null,
                  "created_at": "2026-10-09T15:20:04.000Z",
                  "updated_at": "2026-10-09T15:20:04.000Z",
                  "fetched_at": null,
                  "duplicate": false
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — No file in the upload, or the upload address is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "No file in the upload, or the upload address is malformed."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — The upload address is unknown, used, or expired. Ask for a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "The upload address is unknown, used, or expired. Ask for a new one."
                }
              }
            }
          },
          "409": {
            "description": "`too_many_sources` — The account already holds the most knowledge sources it may.\n\n`knowledge_full` — The account already holds the most knowledge text it may.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_many_sources",
                            "knowledge_full"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_many_sources",
                  "message": "The account already holds the most knowledge sources it may."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The file is over 25 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The file is over 25 MB."
                }
              }
            }
          },
          "422": {
            "description": "`unsupported_type` — Not a file type SlyOS reads (PDF, Word .docx, plain text, Markdown, CSV).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unsupported_type"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unsupported_type",
                  "message": "Not a file type SlyOS reads (PDF, Word .docx, plain text, Markdown, CSV)."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account. Wait a minute.\n\n`busy` — Too many sources are being read at once. Add more when they finish.\n\n`uploads_busy` — Two files are already uploading for this account. Send the next after.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited",
                            "busy",
                            "uploads_busy"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          }
        },
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 30,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos": {
      "post": {
        "operationId": "apiCreateVideo",
        "summary": "Start a video",
        "description": "Start a video: your own script, or none yet. `kind: check` starts one whose only take is a video you want checked. The same `request_id` twice makes one video.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 20 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nRetry safely with the same `request_id`: you get the same object, spent once. A `request_id` reused for different work is refused `409 video_request_reused`.\n\nStarting a video spends nothing. Uploading takes counts footage; making the video spends a video.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — The body does not match `ApiCreateVideoRequest`.\n- `video_not_in_plan` — The account’s plan makes no videos.\n- `video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiCreateVideoRequest"
              },
              "example": {
                "request_id": "5f7b9d1e-3a5c-4e7a-9c1e-3b5d7f9a1c86",
                "platform": "both",
                "target_seconds": 40,
                "script": {
                  "title": "Refills ship free",
                  "lines": [
                    {
                      "text": "You can stop buying new bottles today.",
                      "role": "hook",
                      "on_screen": "Stop buying bottles"
                    },
                    {
                      "text": "Every Example Co bottle refills, and refills now ship free in the US and Canada.",
                      "role": "line",
                      "on_screen": null
                    },
                    {
                      "text": "Orders leave our warehouse within two business days.",
                      "role": "line",
                      "on_screen": null
                    },
                    {
                      "text": "Tap the link in our bio to get your first refill.",
                      "role": "cta",
                      "on_screen": "Link in bio"
                    }
                  ],
                  "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                  "hashtags": [
                    "#refill",
                    "#zerowaste"
                  ],
                  "source_ids": [
                    "0a2c4e6b-8d1f-4a3c-9e5b-7d9f1b3d5f78"
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The video exists — made now, or made before with this `request_id`. Add takes next.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoProjectView"
                },
                "example": {
                  "id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "kind": "make",
                  "title": "Refills ship free",
                  "state": "script",
                  "platform": "both",
                  "target_seconds": 40,
                  "versions": 3,
                  "script": {
                    "title": "Refills ship free",
                    "lines": [
                      {
                        "text": "You can stop buying new bottles today.",
                        "role": "hook",
                        "on_screen": "Stop buying bottles"
                      },
                      {
                        "text": "Every Example Co bottle refills, and refills now ship free in the US and Canada.",
                        "role": "line",
                        "on_screen": null
                      },
                      {
                        "text": "Orders leave our warehouse within two business days.",
                        "role": "line",
                        "on_screen": null
                      },
                      {
                        "text": "Tap the link in our bio to get your first refill.",
                        "role": "cta",
                        "on_screen": "Link in bio"
                      }
                    ],
                    "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                    "hashtags": [
                      "#refill",
                      "#zerowaste"
                    ],
                    "source_ids": [
                      "0a2c4e6b-8d1f-4a3c-9e5b-7d9f1b3d5f78"
                    ]
                  },
                  "takes": [],
                  "job": null,
                  "renders": [],
                  "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                  "hashtags": [
                    "#refill",
                    "#zerowaste"
                  ],
                  "changes_left": 3,
                  "evaluation_ids": [],
                  "created_at": "2026-10-21T18:00:00.000Z",
                  "updated_at": "2026-10-21T18:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The body does not match `ApiCreateVideoRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The body does not match `ApiCreateVideoRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n\n`video_not_in_plan` — The account’s plan makes no videos.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan",
                            "video_not_in_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "409": {
            "description": "`video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_request_reused"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_request_reused",
                  "message": "That `request_id` already started something else. Do not retry with it; use a new one."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 20,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/video-scripts": {
      "post": {
        "operationId": "apiWriteVideoScript",
        "summary": "Have SlyOS write a script",
        "description": "Have SlyOS write a script from a topic — from the business’s About you and the links you send — and start a video with it. The same `request_id` twice is one script.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 20 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nRetry safely with the same `request_id`: you get the same object, spent once. A `request_id` reused for different work is refused `409 video_request_reused`.\n\nWritten from the business’s About you — its rules whole, the passages nearest the topic — and the links you send. A sandbox key gets a fixed example script, written without a model.\n\nA script SlyOS writes is a Brain answer: it is counted as one, and refused with `paused_allowance` while the Brain is paused on the account. A script you write yourself (`POST /developer/videos` with `script`) is never refused for that.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — The body does not match `WriteVideoScriptRequest`.\n- `video_not_in_plan` — The account’s plan makes no videos.\n- `video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.\n- `ai_off` — The account has not allowed AI in SlyOS, so no script can be written.\n- `paused_allowance` — The Brain is paused on this account until its plan renews, and a script SlyOS writes is a Brain answer. Do not retry until it renews; start the video with your own script instead (`POST /developer/videos` with `script`).",
        "tags": [
          "Video scripts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WriteVideoScriptRequest"
              },
              "example": {
                "request_id": "e9b1d3f5-7a9c-4e1b-a3d5-f7b9d1e3a526",
                "topic": "Refills now ship free in the US and Canada",
                "platform": "both",
                "target_seconds": 40,
                "links": [
                  "https://www.example.com/shipping"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The script is written, and a video started with it — or this `request_id` wrote it before.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoProjectView"
                },
                "example": {
                  "id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "kind": "make",
                  "title": "Refills ship free",
                  "state": "script",
                  "platform": "both",
                  "target_seconds": 40,
                  "versions": 3,
                  "script": {
                    "title": "Refills ship free",
                    "lines": [
                      {
                        "text": "You can stop buying new bottles today.",
                        "role": "hook",
                        "on_screen": "Stop buying bottles"
                      },
                      {
                        "text": "Every Example Co bottle refills, and refills now ship free in the US and Canada.",
                        "role": "line",
                        "on_screen": null
                      },
                      {
                        "text": "Orders leave our warehouse within two business days.",
                        "role": "line",
                        "on_screen": null
                      },
                      {
                        "text": "Tap the link in our bio to get your first refill.",
                        "role": "cta",
                        "on_screen": "Link in bio"
                      }
                    ],
                    "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                    "hashtags": [
                      "#refill",
                      "#zerowaste"
                    ],
                    "source_ids": [
                      "0a2c4e6b-8d1f-4a3c-9e5b-7d9f1b3d5f78"
                    ]
                  },
                  "takes": [],
                  "job": null,
                  "renders": [],
                  "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                  "hashtags": [
                    "#refill",
                    "#zerowaste"
                  ],
                  "changes_left": 3,
                  "evaluation_ids": [],
                  "created_at": "2026-10-21T18:00:00.000Z",
                  "updated_at": "2026-10-21T18:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — The body does not match `WriteVideoScriptRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "The body does not match `WriteVideoScriptRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n\n`video_not_in_plan` — The account’s plan makes no videos.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan",
                            "video_not_in_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.\n\n`ai_off` — The account has not allowed AI in SlyOS, so no script can be written.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "ai_off"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "409": {
            "description": "`video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.\n\n`paused_allowance` — The Brain is paused on this account until its plan renews, and a script SlyOS writes is a Brain answer. Do not retry until it renews; start the video with your own script instead (`POST /developer/videos` with `script`).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_request_reused",
                            "paused_allowance"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_request_reused",
                  "message": "That `request_id` already started something else. Do not retry with it; use a new one."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 20,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}": {
      "get": {
        "operationId": "apiGetVideo",
        "summary": "Read a video",
        "description": "Everything about one video: its script, every take and its upload, the job under way, the finished cuts with links to play them, the caption and hashtags.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:read` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nPoll this while a job runs — every few seconds is plenty — or wait for `video.ready`. The `poster_url` and `preview_url` links work for an hour; read the video again for fresh ones.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:read.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The video, as it stands now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoProjectView"
                },
                "example": {
                  "id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "kind": "make",
                  "title": "Refills ship free",
                  "state": "ready",
                  "platform": "both",
                  "target_seconds": 40,
                  "versions": 3,
                  "script": {
                    "title": "Refills ship free",
                    "lines": [
                      {
                        "text": "You can stop buying new bottles today.",
                        "role": "hook",
                        "on_screen": "Stop buying bottles"
                      },
                      {
                        "text": "Every Example Co bottle refills, and refills now ship free in the US and Canada.",
                        "role": "line",
                        "on_screen": null
                      },
                      {
                        "text": "Orders leave our warehouse within two business days.",
                        "role": "line",
                        "on_screen": null
                      },
                      {
                        "text": "Tap the link in our bio to get your first refill.",
                        "role": "cta",
                        "on_screen": "Link in bio"
                      }
                    ],
                    "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                    "hashtags": [
                      "#refill",
                      "#zerowaste"
                    ],
                    "source_ids": [
                      "0a2c4e6b-8d1f-4a3c-9e5b-7d9f1b3d5f78"
                    ]
                  },
                  "takes": [
                    {
                      "id": "7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13",
                      "n": 1,
                      "filename": "take-1.mov",
                      "state": "ready",
                      "seconds": 48.2,
                      "poster_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-work/7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13?response-content-type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "failure": null,
                      "note": null,
                      "upload": null,
                      "created_at": "2026-10-21T18:01:02.000Z"
                    },
                    {
                      "id": "d4f6a8c0-2e4a-4c6e-9b1d-4f6a8c0e2b35",
                      "n": 2,
                      "filename": "take-2.mov",
                      "state": "ready",
                      "seconds": 51.7,
                      "poster_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-work/d4f6a8c0-2e4a-4c6e-9b1d-4f6a8c0e2b35?response-content-type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "failure": null,
                      "note": null,
                      "upload": null,
                      "created_at": "2026-10-21T18:01:02.000Z"
                    }
                  ],
                  "job": {
                    "id": "c5e7a9b1-3d5f-4a7c-8e1b-5d7f9a1c3e57",
                    "kind": "assemble",
                    "state": "succeeded",
                    "step": "finishing",
                    "step_words": "Adding the last touches",
                    "progress": 100,
                    "ahead": null,
                    "failure": null,
                    "created_at": "2026-10-21T18:05:40.000Z",
                    "finished_at": "2026-10-21T18:06:40.000Z"
                  },
                  "renders": [
                    {
                      "id": "1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19",
                      "label": "A",
                      "style": "tight",
                      "state": "ready",
                      "seconds": 34.6,
                      "width": 1080,
                      "height": 1920,
                      "bytes": 20760000,
                      "poster_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19?response-content-type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "preview_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19?response-content-type=video%2Fmp4&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "chosen": true,
                      "kept_until": "2026-11-20T18:06:40.000Z",
                      "created_at": "2026-10-21T18:05:40.000Z"
                    },
                    {
                      "id": "2a4c6e8b-0d1f-4b3a-9c5e-7f9b1d3e5a20",
                      "label": "B",
                      "style": "story",
                      "state": "ready",
                      "seconds": 41.2,
                      "width": 1080,
                      "height": 1920,
                      "bytes": 24720000,
                      "poster_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/2a4c6e8b-0d1f-4b3a-9c5e-7f9b1d3e5a20?response-content-type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "preview_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/2a4c6e8b-0d1f-4b3a-9c5e-7f9b1d3e5a20?response-content-type=video%2Fmp4&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "chosen": false,
                      "kept_until": "2026-11-20T18:06:40.000Z",
                      "created_at": "2026-10-21T18:05:40.000Z"
                    },
                    {
                      "id": "3b5d7f9c-1e2a-4c4b-a6d8-8e0c2f4a6b31",
                      "label": "C",
                      "style": "punchy",
                      "state": "ready",
                      "seconds": 37.9,
                      "width": 1080,
                      "height": 1920,
                      "bytes": 22740000,
                      "poster_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/3b5d7f9c-1e2a-4c4b-a6d8-8e0c2f4a6b31?response-content-type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "preview_url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/3b5d7f9c-1e2a-4c4b-a6d8-8e0c2f4a6b31?response-content-type=video%2Fmp4&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                      "chosen": false,
                      "kept_until": "2026-11-20T18:06:40.000Z",
                      "created_at": "2026-10-21T18:05:40.000Z"
                    }
                  ],
                  "caption": "Refill, don’t rebuy. Free refill shipping in the US and Canada.",
                  "hashtags": [
                    "#refill",
                    "#zerowaste"
                  ],
                  "changes_left": 3,
                  "evaluation_ids": [
                    "6d8f0b2c-4e6a-4d8c-b0e2-a4c6e8f0b243"
                  ],
                  "created_at": "2026-10-21T18:00:00.000Z",
                  "updated_at": "2026-10-21T18:06:40.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      },
      "delete": {
        "operationId": "apiDeleteVideo",
        "summary": "Delete a video",
        "description": "Delete a video: its script, transcripts and checks at once, and every file of it queued for deletion from storage. What it used of the month is not given back.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 30 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiDeleteVideoResponse"
                },
                "example": {
                  "deleted": true
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 30,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/takes": {
      "post": {
        "operationId": "apiOpenVideoTake",
        "summary": "Upload a take",
        "description": "Open the upload of one take, straight to storage in parts: the take, its part size, and addresses for the first parts. Refused before a byte travels when the take is over the plan’s size or length, or the month’s footage.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 30 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nThe file goes straight to storage, never through SlyOS: cut it into 16,777,216-byte parts (the last may be shorter), PUT each part's bytes to its address — three at a time is plenty — and keep each answer's `ETag` header. Then `POST …/complete` with every part's ETag. Addresses work for 15 minutes; ask for more, or fresh ones, with `part-urls`.\n\nInterrupted? `GET /v1/developer/videos/{videoId}/takes/{takeId}` says which parts storage already holds (`upload.parts_done`); upload the rest. A take whose upload is not completed within a day is abandoned.\n\nThe plan’s limits are checked before any upload: a take’s size and length, takes per video, and footage per video and per month. Footage counts as soon as the take is opened, as declared, and is corrected to what SlyOS measures.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `video_sandbox_limit` — A sandbox key may open 20 takes in any 24 hours; the Retry-After header says when the next one may. Nothing was charged.\n- `bad_request` — A path id is not a UUID. Or the body does not match `OpenVideoTakeRequest`.\n- `video_not_in_plan` — The account’s plan makes no videos.\n- `video_footage_used` — This month’s footage is already uploaded.\n- `video_take_too_big` — The take is over the plan’s size or length, or would take the video past its footage. In the sandbox a take is at most 50 MB.\n- `video_too_many_takes` — The video has as many takes as the plan allows.\n- `ai_off` — The account has not allowed AI in SlyOS, so a live take cannot be transcribed. A sandbox take is not asked.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenVideoTakeRequest"
              },
              "example": {
                "filename": "take-1.mov",
                "mime_type": "video/quicktime",
                "size_bytes": 41943040,
                "duration_seconds": 48.2
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The upload is open. PUT each part to its address, then complete the take.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenVideoTakeResponse"
                },
                "example": {
                  "take": {
                    "id": "7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13",
                    "n": 1,
                    "filename": "take-1.mov",
                    "state": "uploading",
                    "seconds": null,
                    "poster_url": null,
                    "failure": null,
                    "note": null,
                    "upload": {
                      "part_bytes": 16777216,
                      "part_count": 3,
                      "parts_done": []
                    },
                    "created_at": "2026-10-21T18:01:02.000Z"
                  },
                  "urls": [
                    {
                      "part_number": 1,
                      "url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-takes/7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13?partNumber=1&uploadId=example&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example"
                    },
                    {
                      "part_number": 2,
                      "url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-takes/7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13?partNumber=2&uploadId=example&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example"
                    },
                    {
                      "part_number": 3,
                      "url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-takes/7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13?partNumber=3&uploadId=example&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example"
                    }
                  ],
                  "expires_at": "2026-10-21T18:16:02.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID. Or the body does not match `OpenVideoTakeRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID. Or the body does not match `OpenVideoTakeRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n\n`video_not_in_plan` — The account’s plan makes no videos.\n\n`video_footage_used` — This month’s footage is already uploaded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan",
                            "video_not_in_plan",
                            "video_footage_used"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.\n\n`ai_off` — The account has not allowed AI in SlyOS, so a live take cannot be transcribed. A sandbox take is not asked.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "ai_off"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "409": {
            "description": "`video_too_many_takes` — The video has as many takes as the plan allows.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_too_many_takes"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_too_many_takes",
                  "message": "The video has as many takes as the plan allows."
                }
              }
            }
          },
          "413": {
            "description": "`video_take_too_big` — The take is over the plan’s size or length, or would take the video past its footage. In the sandbox a take is at most 50 MB.\n\n`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_take_too_big",
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_take_too_big",
                  "message": "The take is over the plan’s size or length, or would take the video past its footage. In the sandbox a take is at most 50 MB."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n\n`video_sandbox_limit` — A sandbox key may open 20 takes in any 24 hours; the Retry-After header says when the next one may. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited",
                            "video_sandbox_limit"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 30,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/takes/{takeId}": {
      "get": {
        "operationId": "apiGetVideoTake",
        "summary": "Read a take",
        "description": "One take: where it stands, and while it uploads, the parts storage already holds — so an upload that was interrupted continues where it stopped.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:read` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:read.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "takeId",
            "in": "path",
            "required": true,
            "description": "One take of that video, as `id` on its `VideoTakeView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The take. While it uploads, `upload.parts_done` lists the parts storage holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoTakeView"
                },
                "example": {
                  "id": "7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13",
                  "n": 1,
                  "filename": "take-1.mov",
                  "state": "uploading",
                  "seconds": null,
                  "poster_url": null,
                  "failure": null,
                  "note": null,
                  "upload": {
                    "part_bytes": 16777216,
                    "part_count": 3,
                    "parts_done": [
                      1,
                      2
                    ]
                  },
                  "created_at": "2026-10-21T18:01:02.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/takes/{takeId}/part-urls": {
      "post": {
        "operationId": "apiVideoPartUrls",
        "summary": "More upload addresses",
        "description": "Addresses for more parts of a take’s upload, or fresh ones for parts whose addresses lapsed. Each works for fifteen minutes.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 240 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID. Or the body does not match `VideoPartUrlsRequest`.\n- `video_upload_incomplete` — A part number past the take’s last part, or a take that is no longer uploading.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "takeId",
            "in": "path",
            "required": true,
            "description": "One take of that video, as `id` on its `VideoTakeView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoPartUrlsRequest"
              },
              "example": {
                "part_numbers": [
                  3
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fresh addresses for the parts asked for.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoPartUrlsResponse"
                },
                "example": {
                  "urls": [
                    {
                      "part_number": 3,
                      "url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-takes/7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13?partNumber=3&uploadId=example&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example"
                    }
                  ],
                  "expires_at": "2026-10-21T18:19:30.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID. Or the body does not match `VideoPartUrlsRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID. Or the body does not match `VideoPartUrlsRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "422": {
            "description": "`video_upload_incomplete` — A part number past the take’s last part, or a take that is no longer uploading.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_upload_incomplete"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_upload_incomplete",
                  "message": "A part number past the take’s last part, or a take that is no longer uploading."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 240,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/takes/{takeId}/complete": {
      "post": {
        "operationId": "apiCompleteVideoTake",
        "summary": "Finish a take’s upload",
        "description": "Finish a take’s upload with every part’s ETag. The take is checked to be the size it was opened for and prepared. Sent again, it answers with the same take.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 30 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nSent again, it answers with the same take.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID. Or the body does not match `CompleteVideoTakeRequest`.\n- `video_upload_incomplete` — Storage does not hold those parts, or the file is not the size the take was opened for.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "takeId",
            "in": "path",
            "required": true,
            "description": "One take of that video, as `id` on its `VideoTakeView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompleteVideoTakeRequest"
              },
              "example": {
                "parts": [
                  {
                    "part_number": 1,
                    "etag": "\"015f2a9c0e7b1d3e5f6a8c9b0d1e2f3a\""
                  },
                  {
                    "part_number": 2,
                    "etag": "\"025f2a9c0e7b1d3e5f6a8c9b0d1e2f3a\""
                  },
                  {
                    "part_number": 3,
                    "etag": "\"035f2a9c0e7b1d3e5f6a8c9b0d1e2f3a\""
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Storage holds the whole take, at the size it was opened for. It is prepared next; read the video until it is `ready`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoTakeView"
                },
                "example": {
                  "id": "7e2a4c6f-1b3d-4f58-9a7c-2e4b6d8f0a13",
                  "n": 1,
                  "filename": "take-1.mov",
                  "state": "uploaded",
                  "seconds": null,
                  "poster_url": null,
                  "failure": null,
                  "note": null,
                  "upload": null,
                  "created_at": "2026-10-21T18:01:02.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID. Or the body does not match `CompleteVideoTakeRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID. Or the body does not match `CompleteVideoTakeRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "422": {
            "description": "`video_upload_incomplete` — Storage does not hold those parts, or the file is not the size the take was opened for.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_upload_incomplete"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_upload_incomplete",
                  "message": "Storage does not hold those parts, or the file is not the size the take was opened for."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 30,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/assemble": {
      "post": {
        "operationId": "apiAssembleVideo",
        "summary": "Make the video",
        "description": "Make the video from its ready takes, or change the last one with your notes. Spends one of the month’s videos — a change spends one only past the plan’s changes per video. The same `request_id` twice is one assembly.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 10 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nOn a live key this spends one of the account’s videos this month — the same monthly count the SlyOS app, the Brain and MCP spend (read it with `GET /v1/developer/video-usage`). Nothing is charged to the API wallet. In the sandbox it spends nothing, at most 20 a day.\n\nA change to a video that has a cut — your notes, \"make it shorter\" — spends no video until the plan’s changes per video are used (`changes_left`).\n\nRetry safely with the same `request_id`: you get the same object, spent once. A `request_id` reused for different work is refused `409 video_request_reused`.\n\nThe cut is 9:16, 1080×1920: the best take of each line, pauses and restarts cut out, the face kept in frame, captions in the business’s look, the hook first. Where the plan makes three versions, three cuts come back to pick from.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `video_sandbox_limit` — A sandbox key may start 20 assemblies and checks in any 24 hours; the Retry-After header says when the next one may. Nothing was charged.\n- `bad_request` — A path id is not a UUID. Or the body does not match `AssembleVideoRequest`.\n- `video_not_in_plan` — The account’s plan makes no videos.\n- `video_month_used` — This month’s videos are made.\n- `video_busy` — A job of this account is already running. Retry with the same `request_id` when it is done.\n- `video_no_takes_ready` — No take is ready to cut yet. Retry with the same `request_id` once a take is `ready`.\n- `video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.\n- `ai_off` — The account has not allowed AI in SlyOS, so a live video cannot be cut. A sandbox video is not asked.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssembleVideoRequest"
              },
              "example": {
                "request_id": "8a0c2e4f-6b8d-4f0a-b2c4-6e8a0c2e4f97",
                "notes": "Open with the free shipping."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued. Read the video until it is `ready`, or wait for `video.ready`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoJobResponse"
                },
                "example": {
                  "job": {
                    "id": "c5e7a9b1-3d5f-4a7c-8e1b-5d7f9a1c3e57",
                    "kind": "assemble",
                    "state": "queued",
                    "step": "waiting",
                    "step_words": "Waiting for a free editor",
                    "progress": null,
                    "ahead": 0,
                    "failure": null,
                    "created_at": "2026-10-21T18:05:40.000Z",
                    "finished_at": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID. Or the body does not match `AssembleVideoRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID. Or the body does not match `AssembleVideoRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n\n`video_not_in_plan` — The account’s plan makes no videos.\n\n`video_month_used` — This month’s videos are made.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan",
                            "video_not_in_plan",
                            "video_month_used"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.\n\n`ai_off` — The account has not allowed AI in SlyOS, so a live video cannot be cut. A sandbox video is not asked.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "ai_off"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "409": {
            "description": "`video_busy` — A job of this account is already running. Retry with the same `request_id` when it is done.\n\n`video_no_takes_ready` — No take is ready to cut yet. Retry with the same `request_id` once a take is `ready`.\n\n`video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_busy",
                            "video_no_takes_ready",
                            "video_request_reused"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_busy",
                  "message": "A job of this account is already running. Retry with the same `request_id` when it is done."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n\n`video_sandbox_limit` — A sandbox key may start 20 assemblies and checks in any 24 hours; the Retry-After header says when the next one may. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited",
                            "video_sandbox_limit"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 10,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/renders/{renderId}/download": {
      "get": {
        "operationId": "apiVideoDownload",
        "summary": "Download a cut",
        "description": "A link that downloads one finished cut as an MP4, minted on the request and good for ten minutes. Fetch it from your server; do not publish it.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:read` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 60 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nMint it when you are about to fetch: it lapses in minutes and is never stored. The cut itself is kept until its `kept_until`.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:read.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "renderId",
            "in": "path",
            "required": true,
            "description": "One finished cut of that video, as `id` in its `renders`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A link that downloads the MP4, good for 10 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoDownloadLink"
                },
                "example": {
                  "url": "https://storage.example.com/0b6f3c2a-5d47-4e81-9a2c-7f1e3d5b8a60/video-outputs/1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19?response-content-disposition=attachment%3B%20filename%3D%22Refills%20ship%20free.mp4%22&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=example",
                  "expires_at": "2026-10-21T18:20:00.000Z",
                  "filename": "Refills ship free.mp4",
                  "bytes": 20760000,
                  "mime_type": "video/mp4"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 60,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/videos/{videoId}/evaluate": {
      "post": {
        "operationId": "apiEvaluateVideo",
        "summary": "Check a video",
        "description": "Check a video against the business’s About you and the links you send: its words and stills are read, and the feedback cites what it relied on. Spends one of the month’s checks. The same `request_id` twice is one check.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:write` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 10 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nOn a live key this spends one of the account’s checks this month — the same monthly count the SlyOS app, the Brain and MCP spend (read it with `GET /v1/developer/video-usage`). Nothing is charged to the API wallet. In the sandbox it spends nothing, at most 20 a day.\n\nTo check a video you did not make with SlyOS: start a video with `kind: check`, upload it as its one take, and check that take. A check reads at most 180 seconds of video.\n\nRetry safely with the same `request_id`: you get the same object, spent once. A `request_id` reused for different work is refused `409 video_request_reused`.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:write.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `video_sandbox_limit` — A sandbox key may start 20 assemblies and checks in any 24 hours; the Retry-After header says when the next one may. Nothing was charged.\n- `bad_request` — A path id is not a UUID. Or the body does not match `EvaluateVideoRequest`.\n- `video_not_in_plan` — The account’s plan makes no videos.\n- `video_checks_used` — This month’s checks are made.\n- `video_busy` — A job of this account is already running. Retry with the same `request_id` when it is done.\n- `video_no_takes_ready` — The video to check is not ready yet. Retry with the same `request_id` once it is.\n- `video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.\n- `ai_off` — The account has not allowed AI in SlyOS, so nothing can be checked.",
        "tags": [
          "Videos"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "videoId",
            "in": "path",
            "required": true,
            "description": "The video, as `id` on its `VideoProjectView`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EvaluateVideoRequest"
              },
              "example": {
                "request_id": "4c6e8a0b-2d4f-4a6c-8e0a-2c4e6a8c0e15",
                "subject": {
                  "render_id": "1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19"
                },
                "links": [
                  "https://www.example.com/shipping"
                ],
                "focus": "Is the shipping right?"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Started. Read the check until it is `ready`, or wait for `video.evaluated`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateVideoResponse"
                },
                "example": {
                  "evaluation": {
                    "id": "6d8f0b2c-4e6a-4d8c-b0e2-a4c6e8f0b243",
                    "project_id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                    "subject": {
                      "render_id": "1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19"
                    },
                    "state": "checking",
                    "body": null,
                    "failure": null,
                    "created_at": "2026-10-21T18:08:00.000Z"
                  },
                  "job": {
                    "id": "9e1a3c5d-7f9b-4b1d-8e3f-5a7c9e1b3d64",
                    "kind": "evaluate",
                    "state": "queued",
                    "step": "waiting",
                    "step_words": "Waiting for a free editor",
                    "progress": null,
                    "ahead": 0,
                    "failure": null,
                    "created_at": "2026-10-21T18:05:40.000Z",
                    "finished_at": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID. Or the body does not match `EvaluateVideoRequest`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID. Or the body does not match `EvaluateVideoRequest`."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n\n`video_not_in_plan` — The account’s plan makes no videos.\n\n`video_checks_used` — This month’s checks are made.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan",
                            "video_not_in_plan",
                            "video_checks_used"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:write.\n\n`ai_off` — The account has not allowed AI in SlyOS, so nothing can be checked.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope",
                            "ai_off"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:write."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "409": {
            "description": "`video_busy` — A job of this account is already running. Retry with the same `request_id` when it is done.\n\n`video_no_takes_ready` — The video to check is not ready yet. Retry with the same `request_id` once it is.\n\n`video_request_reused` — That `request_id` already started something else. Do not retry with it; use a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_busy",
                            "video_no_takes_ready",
                            "video_request_reused"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_busy",
                  "message": "A job of this account is already running. Retry with the same `request_id` when it is done."
                }
              }
            }
          },
          "413": {
            "description": "`too_large` — The request body is larger than this endpoint accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "too_large",
                  "message": "The request body is larger than this endpoint accepts."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n\n`video_sandbox_limit` — A sandbox key may start 20 assemblies and checks in any 24 hours; the Retry-After header says when the next one may. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited",
                            "video_sandbox_limit"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:write",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 10,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/video-evaluations/{evaluationId}": {
      "get": {
        "operationId": "apiGetVideoEvaluation",
        "summary": "Read a check",
        "description": "One check: still checking, the feedback with the references it cites, or why it failed.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:read` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\nEvery `cites` names a reference in `references`; a check that cited anything else is refused before it is stored, so a citation is never invented.\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:read.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.\n- `bad_request` — A path id is not a UUID.",
        "tags": [
          "Video evaluations"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "evaluationId",
            "in": "path",
            "required": true,
            "description": "The check, as `evaluation.id` in the answer that started it.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The check: still `checking`, `ready` with its feedback, or `failed` with why.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoEvaluationView"
                },
                "example": {
                  "id": "6d8f0b2c-4e6a-4d8c-b0e2-a4c6e8f0b243",
                  "project_id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "subject": {
                    "render_id": "1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19"
                  },
                  "state": "ready",
                  "body": {
                    "verdict": "good",
                    "summary": "A clear, friendly video with one wrong fact: refills ship free in the US and Canada, not everywhere.",
                    "hook": {
                      "works": true,
                      "why": "It names the viewer’s problem in the first second.",
                      "instead": null
                    },
                    "strengths": [
                      {
                        "point": "It ends on one plain thing to do.",
                        "at_seconds": 31.5,
                        "cites": []
                      }
                    ],
                    "fixes": [
                      {
                        "point": "It says refills ship free everywhere; the shipping page says the US and Canada.",
                        "at_seconds": 9.4,
                        "cites": [
                          1
                        ],
                        "weight": "must",
                        "instead": "Refills now ship free in the US and Canada."
                      },
                      {
                        "point": "The pause before the last line is long.",
                        "at_seconds": 28.2,
                        "cites": [],
                        "weight": "could",
                        "instead": null
                      }
                    ],
                    "claims": [
                      {
                        "claim": "Refills ship free everywhere.",
                        "at_seconds": 9.4,
                        "status": "contradicted",
                        "cites": [
                          1
                        ]
                      },
                      {
                        "claim": "Orders leave the warehouse within two business days.",
                        "at_seconds": 17.8,
                        "status": "supported",
                        "cites": [
                          1
                        ]
                      }
                    ],
                    "references": [
                      {
                        "n": 1,
                        "kind": "about_link",
                        "label": "Example Co — shipping",
                        "source_id": "0a2c4e6b-8d1f-4a3c-9e5b-7d9f1b3d5f78",
                        "url": "https://www.example.com/shipping",
                        "excerpt": "We ship to the US and Canada. Orders leave our warehouse within two business days."
                      }
                    ]
                  },
                  "failure": null,
                  "created_at": "2026-10-21T18:08:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`bad_request` — A path id is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "bad_request"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "bad_request",
                  "message": "A path id is not a UUID."
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      }
    },
    "/v1/developer/video-usage": {
      "get": {
        "operationId": "apiVideoUsage",
        "summary": "This month’s videos",
        "description": "What the account’s plan includes and what this month has used — videos and checks as counts, footage as a percentage — and when they reset. One meter for every door: the app, the Brain, this API and MCP.\n\n- **Auth:** an API key (`sly_test_…` or `sly_live_…`) carrying the `video:read` scope.\n- **Entitlement:** a plan with the Brain and video (free has neither); every door spends the same monthly videos and checks\n- **Rate limit:** 120 requests a minute; past it, `429 rate_limited`\n- **Cost:** free\n\n**Errors**\n- `unauthorized` — The key is missing, malformed, revoked, or not one we issued.\n- `missing_scope` — The key does not carry video:read.\n- `brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.\n- `not_found` — No such video in this key’s environment and project, or video is not switched on for this account.\n- `rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.\n- `video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
        "tags": [
          "Video usage"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "What the plan includes and what this month has used.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoUsageView"
                },
                "example": {
                  "included": true,
                  "videos": {
                    "used": 3,
                    "of": 15
                  },
                  "checks": {
                    "used": 2,
                    "of": 15
                  },
                  "footage_pct": 40,
                  "resets_at": "2026-11-01T00:00:00.000Z",
                  "versions": 3,
                  "keep_days": 30,
                  "take_max_bytes": 4294967296,
                  "take_max_minutes": 10,
                  "output_max_seconds": 120
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized` — The key is missing, malformed, revoked, or not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "unauthorized"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "unauthorized",
                  "message": "The key is missing, malformed, revoked, or not one we issued."
                }
              }
            }
          },
          "402": {
            "description": "`brain_requires_paid_plan` — The account’s plan has no Brain, and video is part of the Brain. The message says so in words.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "brain_requires_paid_plan"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "brain_requires_paid_plan",
                  "message": "The account’s plan has no Brain, and video is part of the Brain. The message says so in words."
                }
              }
            }
          },
          "403": {
            "description": "`missing_scope` — The key does not carry video:read.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "missing_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "missing_scope",
                  "message": "The key does not carry video:read."
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such video in this key’s environment and project, or video is not switched on for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "not_found",
                  "message": "No such video in this key’s environment and project, or video is not switched on for this account."
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` — Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Over this route’s per-minute limit, counted per account and per key, sandbox included. Wait a minute."
                }
              }
            }
          },
          "500": {
            "description": "`internal` — An unexpected fault on SlyOS’s side. It is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "internal"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected fault on SlyOS’s side. It is recorded."
                }
              }
            }
          },
          "503": {
            "description": "`video_unavailable` — SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiRefusal"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "video_unavailable"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "code": "video_unavailable",
                  "message": "SlyOS cannot make videos right now: no store or render worker is reachable. Nothing was spent. Retry later."
                }
              }
            }
          }
        },
        "x-slyos-scope": "video:read",
        "x-slyos-billable": false,
        "x-slyos-rate-limit-per-minute": 120,
        "x-slyos-rate-limit-enforced": true
      }
    }
  },
  "webhooks": {
    "draft.ready": {
      "post": {
        "operationId": "webhook_draft_ready",
        "summary": "A reply is ready",
        "description": "Sent after `POST /v1/developer/drafts` writes a reply, to the endpoint set for the environment of the key that asked — a sandbox reply to the sandbox endpoint, a live one to the live endpoint. Retried until it is answered `2xx`, under the same `id`: deduplicate on it. It carries the hold: send automatically only when `held_for_review` is `false`; `true` or `null` goes to a person. Read the reply’s text with `GET /v1/developer/drafts/{draftId}`, which carries the same hold. The same reply also came back in the `POST` answer: if your integration sends from that answer, do not send it again from this event.\n\nSigned with `SlyOS-Signature`; verify it before reading a single field of the body.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "SlyOS-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex>`. The `v1` value is HMAC-SHA256, keyed with your endpoint's signing secret, of the string `\"<t>.<raw request body>\"`.\n\nTo verify: split on commas; read `t`; refuse the delivery if `t` is more than 300 seconds from your clock; compute the HMAC over the RAW body bytes you received (not re-serialised JSON); accept if it equals ANY `v1=` value, compared in constant time.\n\nDuring a secret rotation the header carries two `v1=` values, one per secret, until the old one expires — which is why the rule is \"any\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DraftReadyEvent"
              },
              "example": {
                "id": "8c2f6e1a-4b7d-4a93-9e05-3f1c7a2d6b58",
                "type": "draft.ready",
                "created": 1791560531,
                "environment": "live",
                "data": {
                  "draft_id": "e1f7a3c5-9d2b-4e68-b0a4-6c3e8f1d2a97",
                  "card_id": "5e8d1c3a-7f24-4b9e-8c61-0a3f9d2e7b14",
                  "generation_id": "9b2e4f71-3c8a-4d5e-a1f6-2e7b9c0d4a18:reply-1",
                  "generation": 1,
                  "environment": "live",
                  "held_for_review": false,
                  "hold_reasons": [],
                  "hold_detail": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer quickly and do the work afterwards. Any other answer, or none, is recorded as a failed attempt on the Developer screen."
          }
        }
      }
    },
    "video.ready": {
      "post": {
        "operationId": "webhook_video_ready",
        "summary": "A video is cut",
        "description": "Sent when a video a key asked for has its cuts — one, or three to pick from — to the endpoint of that key’s environment. Retried until it is answered `2xx`, under the same `id`: deduplicate on it. Mint a download link for each cut with `GET /v1/developer/videos/{videoId}/renders/{renderId}/download`. A video made in the SlyOS app sends none.\n\nSigned with `SlyOS-Signature`; verify it before reading a single field of the body.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "SlyOS-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex>`. The `v1` value is HMAC-SHA256, keyed with your endpoint's signing secret, of the string `\"<t>.<raw request body>\"`.\n\nTo verify: split on commas; read `t`; refuse the delivery if `t` is more than 300 seconds from your clock; compute the HMAC over the RAW body bytes you received (not re-serialised JSON); accept if it equals ANY `v1=` value, compared in constant time.\n\nDuring a secret rotation the header carries two `v1=` values, one per secret, until the old one expires — which is why the rule is \"any\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoReadyEvent"
              },
              "example": {
                "id": "f1a3c5e7-9b2d-4f6a-8c0e-2a4c6e8f0b12",
                "type": "video.ready",
                "created": 1792605600,
                "environment": "live",
                "data": {
                  "video_id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "render_ids": [
                    "1f3b5d7a-9c2e-4a4c-8e6b-0d2f4a6c8e19",
                    "2a4c6e8b-0d1f-4b3a-9c5e-7f9b1d3e5a20",
                    "3b5d7f9c-1e2a-4c4b-a6d8-8e0c2f4a6b31"
                  ],
                  "environment": "live"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer quickly and do the work afterwards. Any other answer, or none, is recorded as a failed attempt on the Developer screen."
          }
        }
      }
    },
    "video.failed": {
      "post": {
        "operationId": "webhook_video_failed",
        "summary": "A video could not be made",
        "description": "Sent when an assembly a key asked for fails for good, to that key’s environment’s endpoint, with the reason in a sentence. The video it would have spent is given back. Retried until answered `2xx`, under the same `id`.\n\nSigned with `SlyOS-Signature`; verify it before reading a single field of the body.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "SlyOS-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex>`. The `v1` value is HMAC-SHA256, keyed with your endpoint's signing secret, of the string `\"<t>.<raw request body>\"`.\n\nTo verify: split on commas; read `t`; refuse the delivery if `t` is more than 300 seconds from your clock; compute the HMAC over the RAW body bytes you received (not re-serialised JSON); accept if it equals ANY `v1=` value, compared in constant time.\n\nDuring a secret rotation the header carries two `v1=` values, one per secret, until the old one expires — which is why the rule is \"any\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoFailedEvent"
              },
              "example": {
                "id": "f1a3c5e7-9b2d-4f6a-8c0e-2a4c6e8f0b12",
                "type": "video.failed",
                "created": 1792605600,
                "environment": "live",
                "data": {
                  "video_id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "job_id": "c5e7a9b1-3d5f-4a7c-8e1b-5d7f9a1c3e57",
                  "failure": "None of the takes had speech SlyOS could hear. Record again somewhere quieter.",
                  "environment": "live"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer quickly and do the work afterwards. Any other answer, or none, is recorded as a failed attempt on the Developer screen."
          }
        }
      }
    },
    "video.evaluated": {
      "post": {
        "operationId": "webhook_video_evaluated",
        "summary": "A check is done",
        "description": "Sent when a check a key asked for has its feedback, to that key’s environment’s endpoint. Read it with `GET /v1/developer/video-evaluations/{evaluationId}`. Retried until answered `2xx`, under the same `id`.\n\nSigned with `SlyOS-Signature`; verify it before reading a single field of the body.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "SlyOS-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex>`. The `v1` value is HMAC-SHA256, keyed with your endpoint's signing secret, of the string `\"<t>.<raw request body>\"`.\n\nTo verify: split on commas; read `t`; refuse the delivery if `t` is more than 300 seconds from your clock; compute the HMAC over the RAW body bytes you received (not re-serialised JSON); accept if it equals ANY `v1=` value, compared in constant time.\n\nDuring a secret rotation the header carries two `v1=` values, one per secret, until the old one expires — which is why the rule is \"any\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoEvaluatedEvent"
              },
              "example": {
                "id": "f1a3c5e7-9b2d-4f6a-8c0e-2a4c6e8f0b12",
                "type": "video.evaluated",
                "created": 1792605600,
                "environment": "live",
                "data": {
                  "video_id": "a3c1e5f7-2b4d-4e6f-8a1c-3e5f7a9b1d24",
                  "evaluation_id": "6d8f0b2c-4e6a-4d8c-b0e2-a4c6e8f0b243",
                  "verdict": "good",
                  "environment": "live"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer quickly and do the work afterwards. Any other answer, or none, is recorded as a failed attempt on the Developer screen."
          }
        }
      }
    },
    "webhook.test": {
      "post": {
        "operationId": "webhook_webhook_test",
        "summary": "A test delivery",
        "description": "Sent when the account owner presses Send test on the Developer screen, to that environment’s endpoint. Sent once and never retried. Nothing happened; use it to check your signature verification.\n\nSigned with `SlyOS-Signature`; verify it before reading a single field of the body.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "SlyOS-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex>`. The `v1` value is HMAC-SHA256, keyed with your endpoint's signing secret, of the string `\"<t>.<raw request body>\"`.\n\nTo verify: split on commas; read `t`; refuse the delivery if `t` is more than 300 seconds from your clock; compute the HMAC over the RAW body bytes you received (not re-serialised JSON); accept if it equals ANY `v1=` value, compared in constant time.\n\nDuring a secret rotation the header carries two `v1=` values, one per secret, until the old one expires — which is why the rule is \"any\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookTestEvent"
              },
              "example": {
                "id": "1a9d3f7c-6e2b-4c48-8b1f-5d0e9a3c7f26",
                "type": "webhook.test",
                "created": 1791560400,
                "environment": "sandbox",
                "data": {
                  "message": "This is a test delivery from SlyOS. Nothing happened."
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer quickly and do the work afterwards. Any other answer, or none, is recorded as a failed attempt on the Developer screen."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sly_test_… or sly_live_…",
        "description": "A Developer API key, sent as `Authorization: Bearer <key>`. Keys starting `sly_test_` are the sandbox — free, and they reach nobody. Keys starting `sly_live_` are live. Create keys in SlyOS under Settings → Plan → Developer; each is shown once."
      }
    },
    "schemas": {
      "ApiAcknowledgeDeliveryRequest": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The SlyOS `message_id` the reply answered — the id ingest returned."
          },
          "status": {
            "type": "string",
            "enum": [
              "delivered",
              "read",
              "failed"
            ],
            "description": "What happened, as your system saw it."
          },
          "detail": {
            "description": "Your system’s own note — a provider’s error text, for example.",
            "type": "string",
            "maxLength": 512
          },
          "occurred_at": {
            "description": "When it happened: ISO 8601 in UTC, ending in `Z`.",
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
          },
          "draft_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The SlyOS draft you sent, as it was. The reply then appears in the conversation, and the card it answered closes as sent."
          },
          "body": {
            "description": "The text you actually sent, when a person edited the draft or wrote it. Wins over `draft_id`'s text when both are given. At most 32,000 characters.",
            "type": "string",
            "minLength": 1,
            "maxLength": 32000
          },
          "external_message_id": {
            "description": "Your own id for the message you sent. Reporting the same one again is still one message in the conversation.",
            "type": "string",
            "minLength": 1,
            "maxLength": 512
          }
        },
        "required": [
          "message_id",
          "status"
        ]
      },
      "ApiAcknowledgeDeliveryResponse": {
        "type": "object",
        "properties": {
          "delivery_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "SlyOS’s record of this report."
          },
          "message_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The message the report was about."
          },
          "status": {
            "type": "string",
            "enum": [
              "delivered",
              "read",
              "failed"
            ],
            "description": "The status you reported."
          },
          "recorded": {
            "type": "boolean",
            "const": true,
            "description": "Always true on a 200: the report was stored."
          },
          "outbound_message_id": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The reply as it now appears in the conversation."
              },
              {
                "type": "null"
              }
            ],
            "description": "The reply as it now appears in the conversation. Null when no text was given (`draft_id` or `body`), or the status was not `delivered`."
          },
          "draft_id": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The SlyOS draft this was."
              },
              {
                "type": "null"
              }
            ],
            "description": "The SlyOS draft this was — named by `draft_id`, or matched by its words. Null when the words were not SlyOS’s."
          },
          "card_closed": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The card this reply closed."
              },
              {
                "type": "null"
              }
            ],
            "description": "The card this reply closed as sent. Null when it closed none."
          }
        },
        "required": [
          "delivery_id",
          "message_id",
          "status",
          "recorded",
          "outbound_message_id",
          "draft_id",
          "card_closed"
        ]
      },
      "ApiAddKnowledgeFileRequest": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "contentEncoding": "binary",
            "description": "The file — PDF, Word (.docx), plain text, Markdown or CSV, up to the size this operation states. It is read to the end; a long one is cut into passages, not cut short."
          },
          "rules": {
            "description": "`true` when replies must follow this file rather than draw on it. Default `false`.",
            "type": "string",
            "enum": [
              "true",
              "false"
            ]
          },
          "title": {
            "description": "What to call it on the Developer screen and in a reply’s `sources`.",
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "project",
              "account"
            ],
            "description": "`project`: replies asked for with this key’s project use it. `account`: replies in every project on the account do."
          }
        },
        "required": [
          "file"
        ]
      },
      "ApiAddKnowledgeLinkRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "A public http(s) address: one page — your pricing page, a policy, a regulation — or, with `crawl`, the start of a whole site."
          },
          "crawl": {
            "description": "Read the whole site from this address: the same site only, robots.txt respected, up to the page limit this operation states. Default: this page alone.",
            "type": "boolean"
          },
          "rules": {
            "description": "True when replies must FOLLOW this source — terms, a policy, a regulation — rather than draw on it as reference. Default false.",
            "type": "boolean"
          },
          "title": {
            "description": "What to call it on the Developer screen and in a reply’s `sources`.",
            "type": "string",
            "maxLength": 300
          },
          "scope": {
            "type": "string",
            "enum": [
              "project",
              "account"
            ],
            "description": "`project`: replies asked for with this key’s project use it. `account`: replies in every project on the account do."
          }
        },
        "required": [
          "url"
        ]
      },
      "ApiAddKnowledgeResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The source’s id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "link",
              "site",
              "file"
            ],
            "description": "`link`: one page. `site`: a whole site, read from one address. `file`: an upload."
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The address it is read from. Null for a file."
          },
          "filename": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The uploaded file’s name. Null for a link or a site."
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What it is called on the Developer screen and in a reply’s `sources`."
          },
          "rules": {
            "type": "boolean",
            "description": "True when replies must follow it; false when it is reference material."
          },
          "scope": {
            "type": "string",
            "enum": [
              "project",
              "account"
            ],
            "description": "`project`: replies asked for with this key’s project use it. `account`: replies in every project on the account do."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "ready",
              "failed"
            ],
            "description": "`queued`, then `processing`, while it is read in the background; `ready` once replies use it; `failed` when it could not be read, with `status_reason` saying why."
          },
          "status_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "In a sentence, why the status is what it is. Null when there is nothing to say."
          },
          "warning": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set when the source is `ready` but some of what it shows may be missing, in a sentence naming the pages and what to do — today, pages that build their content with JavaScript, which SlyOS does not run (a price list drawn from a script, a template filled in after the page loads). Null when there is nothing to warn about."
          },
          "incomplete_pages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The addresses of the pages `warning` names: pages whose content is built with JavaScript, so replies may be missing what a visitor sees there. Add that text as a file, or as a single link to a page that shows it. Empty when there are none."
          },
          "pages": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Pages read from it."
          },
          "passages": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Passages it was cut into: what a reply retrieves from."
          },
          "characters": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Characters of text read from it."
          },
          "size_bytes": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "The uploaded file’s size. Null for a link or a site."
          },
          "content_sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "SHA-256 of the text read from it; it changes when a refresh reads something new."
          },
          "file_sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "SHA-256 of the uploaded file’s bytes. Null for a link or a site."
          },
          "used_in_replies": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "How many replies have relied on a passage of it."
          },
          "rules_fit": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "For a rules source: true when every one of the business’s rules reaches every reply; false when they no longer fit in one reply, and each reply gets the rules nearest its message. Null for reference material, or when it was not measured."
          },
          "rules_note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When `rules_fit` is false, in a sentence: how much the rules come to, what one reply carries, and what to do about it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "description": "When it was added."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "description": "When it last changed."
          },
          "fetched_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                "description": "When it was last read. Null until it has been."
              },
              {
                "type": "null"
              }
            ]
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when this address or this exact file was already a source: nothing new was added, and this is that source."
          }
        },
        "required": [
          "id",
          "kind",
          "url",
          "filename",
          "title",
          "rules",
          "scope",
          "status",
          "status_reason",
          "warning",
          "incomplete_pages",
          "pages",
          "passages",
          "characters",
          "size_bytes",
          "content_sha256",
          "file_sha256",
          "used_in_replies",
          "rules_fit",
          "rules_note",
          "created_at",
          "updated_at",
          "fetched_at",
          "duplicate"
        ]
      },
      "ApiBusinessProfile": {
        "type": "object",
        "properties": {
          "business_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The business’s name, as replies give it."
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the business is and does, in its own words."
          },
          "tone_notes": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "How replies should sound."
          },
          "use_personal_context": {
            "type": "boolean",
            "description": "Whether API replies also use everything SlyOS knows about the account owner: facts from their own conversations and the ones they told it, their past replies on their other channels, their documents, calendar and saved details, and their About you. On unless the owner turned it off on the Developer screen; a key cannot change it. Off, a reply still uses the owner’s name, rules and writing style. Either way, nothing one customer wrote or was sent goes into another customer’s reply, and nothing from a sandbox conversation goes into a live one; every reply the account sends still shapes its writing style."
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                "description": "When the profile last changed. Null if it never has."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "business_name",
          "description",
          "tone_notes",
          "use_personal_context",
          "updated_at"
        ]
      },
      "ApiChannel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The channel’s id: the `channel_id` every message sent to it names."
          },
          "name": {
            "type": "string",
            "description": "Its name on every SlyOS screen."
          },
          "system": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The system behind it, if you named one."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "The environment of the key that made the call: `sandbox` or `live`."
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "receive",
                "send",
                "attachments",
                "read_receipts",
                "mark_read",
                "delivery_receipts",
                "reactions",
                "typing",
                "threading",
                "groups"
              ]
            },
            "description": "What the adapter declared it can do."
          }
        },
        "required": [
          "id",
          "name",
          "system",
          "environment",
          "capabilities"
        ]
      },
      "ApiCreateChannelRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "What the channel is called on every SlyOS screen."
          },
          "system": {
            "description": "The system behind it, for your own reference — “Zendesk”, “our app chat”.",
            "type": "string",
            "maxLength": 120
          },
          "capabilities": {
            "description": "What your adapter can actually do on this channel. SlyOS never assumes one it was not told. Defaults to `[\"receive\", \"send\"]`.",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "receive",
                "send",
                "attachments",
                "read_receipts",
                "mark_read",
                "delivery_receipts",
                "reactions",
                "typing",
                "threading",
                "groups"
              ]
            }
          }
        },
        "required": [
          "name"
        ]
      },
      "ApiCreateVideoRequest": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "Your idempotency id, a UUID. The same one twice makes one video; reusing it for different work is refused."
          },
          "kind": {
            "default": "make",
            "type": "string",
            "enum": [
              "make",
              "check"
            ],
            "description": "`make` (the default) or `check`."
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "What it is called. Defaults to the script’s title."
          },
          "platform": {
            "default": "both",
            "type": "string",
            "enum": [
              "tiktok",
              "instagram",
              "both"
            ],
            "description": "`tiktok`, `instagram` or `both` (the default)."
          },
          "target_seconds": {
            "default": 45,
            "type": "integer",
            "minimum": 10,
            "maximum": 180,
            "description": "The length to aim for, 10–180 seconds. Default 45."
          },
          "script": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 120,
                "description": "What the video is called. The downloaded file is named after it."
              },
              "lines": {
                "minItems": 1,
                "maxItems": 30,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 400,
                      "description": "What is said."
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "hook",
                        "line",
                        "cta"
                      ],
                      "description": "`hook` opens the video wherever it sits in the script; `cta` closes it; `line` is everything between. At most one hook."
                    },
                    "on_screen": {
                      "anyOf": [
                        {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 60
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "Big words shown while the line is said, or null."
                    }
                  },
                  "required": [
                    "text",
                    "role",
                    "on_screen"
                  ],
                  "additionalProperties": false
                },
                "description": "The spoken lines, in the order they are written."
              },
              "caption": {
                "type": "string",
                "maxLength": 2200,
                "description": "The caption to post with it — up to 2,200 characters, Instagram’s limit."
              },
              "hashtags": {
                "maxItems": 10,
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^#[\\p{L}\\p{N}_]{1,49}$"
                },
                "description": "Up to 10 hashtags, each `#` and letters, digits or `_`."
              },
              "source_ids": {
                "maxItems": 10,
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "description": "The About-you sources the script drew on, by id, so its facts can be traced. Empty when it drew on none."
              }
            },
            "required": [
              "title",
              "lines",
              "caption",
              "hashtags",
              "source_ids"
            ],
            "additionalProperties": false,
            "description": "Your own script. Leave it out to add takes without one."
          }
        },
        "required": [
          "request_id"
        ],
        "additionalProperties": false
      },
      "ApiDeleteKnowledgeResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The source that was deleted."
          },
          "deleted": {
            "type": "boolean",
            "const": true,
            "description": "Always true on a 200."
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "ApiDeleteVideoResponse": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "boolean",
            "const": true,
            "description": "Always true. The video’s rows are gone and its files are queued for deletion from storage."
          }
        },
        "required": [
          "deleted"
        ]
      },
      "ApiGenerateDraftRequest": {
        "type": "object",
        "properties": {
          "generation_id": {
            "type": "string",
            "minLength": 8,
            "maxLength": 128,
            "description": "Your id for this one reply, and the idempotency key for the charge: sending the same `generation_id` again does not buy a second reply. It is yours alone — unique within your account and the key’s environment, never shared with another account. Make it unique and reproducible — derive it from the `message_id` SlyOS gave you, e.g. `<message_id>:reply-1`, and use `:reply-2` for a deliberate second reply. An id already used for a different conversation is refused (409 `generation_id_conflict`), never answered with that conversation’s reply."
          },
          "card_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The card to reply on. Give this or `message_id`."
          },
          "message_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "A `message_id` from `POST /v1/developer/messages`. SlyOS replies on the card that message opened. Give this or `card_id`."
          },
          "instruction": {
            "description": "A one-off steer for this reply, in plain words — for example “Offer a call on Thursday.” At most 2,000 characters.",
            "type": "string",
            "maxLength": 2000
          }
        },
        "required": [
          "generation_id"
        ]
      },
      "ApiGenerateDraftResponse": {
        "type": "object",
        "properties": {
          "draft_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The draft’s id. `GET /v1/developer/drafts/{draftId}` reads it back."
          },
          "card_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The card the reply is for."
          },
          "generation": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "Which draft this is on the card: 1 for the first, higher after another reply was asked for."
          },
          "body": {
            "type": "string",
            "description": "The reply text."
          },
          "held_for_review": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "True when SlyOS’s checks held this reply for a person — money, health or dosing, a legal or data-protection matter, another sensitive subject, or something it gives out that the account has not sent before. Never send a held reply automatically: show it to a person first. Null when SlyOS can no longer say, because the conversation has had a newer reply since; null means held. Send automatically only on `false`."
          },
          "hold_reasons": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "Which check held it: `sensitive` (money, legal, health or credentials), `attachment` (it carries a file), `group` (it goes to a group), `regex_floor` (it matches a rule you set), `outbound_screen` (it gives out something not sent before, or answers a risky request). A code not listed here is still a hold — treat every code you do not recognise as one."
                },
                "label": {
                  "type": "string",
                  "description": "The same reason in words, as the SlyOS inbox shows it."
                }
              },
              "required": [
                "code",
                "label"
              ]
            },
            "description": "Why it was held: one entry per check that held it. Empty when `held_for_review` is not `true`."
          },
          "hold_detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the checks found, in sentences, when the reply was held. Null when it was not held, or when SlyOS cannot say."
          },
          "billed_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "What THIS request was charged: the price on a live key’s first success, 0 on a replay and in the sandbox."
          },
          "already_charged": {
            "type": "boolean",
            "description": "True when this `generation_id` had already been paid for and this response is that same reply again."
          },
          "originally_billed_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Only on a replay: what the original generation was charged."
          },
          "price_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The price version the charge was made under. Null in the sandbox."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "The environment of the key that made the call: `sandbox` or `live`."
          },
          "replayed": {
            "type": "boolean",
            "description": "True when this answer is a reply SlyOS already wrote for this `generation_id` — in the sandbox too, where nothing is charged. It may already have been sent: do not send it again automatically."
          },
          "generation_id": {
            "type": "string",
            "description": "The `generation_id` you sent, echoed."
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                      "description": "The knowledge source the passage came from. Null once that source is deleted."
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "passage_id": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                      "description": "The passage itself. Null once its source is deleted."
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "title": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "What the source was called when the reply was written: its title, file name or address."
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The page the passage was read from, for a link or a site."
                },
                "page": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The page of the file its own text starts on, for a file that has pages. With `page_end`, the range it covers."
                },
                "page_end": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The last page it runs onto, when it runs past `page` — a passage starting on page 11 and carrying page 12 is `page: 11, page_end: 12` (p.11–12). Null when it stays on one page, or the file has no pages."
                },
                "section": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The heading it sat under, if any."
                },
                "rule": {
                  "type": "boolean",
                  "description": "True for a binding rule the reply had to follow; false for reference material it could draw on."
                },
                "rank": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Its place in what the reply was given, from 1 — counted separately for rules and for reference."
                },
                "score": {
                  "anyOf": [
                    {
                      "type": "number"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How close the passage was to the message, as cosine similarity. Null when there was nothing to compare."
                },
                "excerpt": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Up to 280 characters of the passage’s OWN text — past the lines it repeats from the passage before it — from the sentence that shares the most words with the reply, else from its start; `…` marks a cut. While its source still exists."
                },
                "text_sha256": {
                  "type": "string",
                  "description": "SHA-256 of the stored passage the reply was given — all of it, or its first `carried_chars` characters: proof of which text it relied on, even after the source changes."
                },
                "carried_chars": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How many of the passage’s characters the reply was given, when it was cut to fit. Null when it was given the whole passage."
                }
              },
              "required": [
                "source_id",
                "passage_id",
                "title",
                "url",
                "page",
                "page_end",
                "section",
                "rule",
                "rank",
                "score",
                "excerpt",
                "text_sha256",
                "carried_chars"
              ]
            },
            "description": "What this reply relied on: every passage of the business’s own knowledge — the links, sites and files it handed SlyOS, and its rules — that the reply was given, rules first. Empty when the reply was given none: the business has handed SlyOS nothing yet, or nothing it handed over matched."
          }
        },
        "required": [
          "draft_id",
          "card_id",
          "generation",
          "body",
          "held_for_review",
          "hold_reasons",
          "hold_detail",
          "billed_cents",
          "already_charged",
          "price_version",
          "environment",
          "replayed",
          "generation_id",
          "sources"
        ]
      },
      "ApiIngestMessageRequest": {
        "type": "object",
        "properties": {
          "channel_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The custom channel the message arrived on: an `id` from `GET /v1/developer/channels`. It must belong to the same environment as the key."
          },
          "external_thread_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Your id for the conversation — a ticket, a chat. Messages that share one are one conversation in SlyOS."
          },
          "external_message_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Your id for this message. Sending the same one on the same channel again stores nothing new and answers 200 with `duplicate: true`, so a retry is always safe."
          },
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 32000,
            "description": "The message text, as the sender wrote it. At most 32,000 characters."
          },
          "sent_at": {
            "description": "When it was sent: ISO 8601 in UTC, ending in `Z`. An offset such as `+01:00` is refused. Defaults to when SlyOS received it.",
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
          },
          "sender_identifier": {
            "description": "Who sent it, as your system knows them — an email address, a phone number or a handle. SlyOS uses it to recognise the same person across messages. Leave it out rather than send it blank.",
            "type": "string",
            "maxLength": 512,
            "pattern": "\\S"
          },
          "sender_display_name": {
            "description": "The sender’s name, as it should be shown.",
            "type": "string",
            "maxLength": 256
          },
          "subject": {
            "description": "A subject line, for channels that have one.",
            "type": "string",
            "maxLength": 512
          },
          "thread_title": {
            "description": "A title for the conversation, shown in the SlyOS inbox.",
            "type": "string",
            "maxLength": 512
          },
          "direction": {
            "default": "inbound",
            "description": "`inbound` (the default) for a message your customer sent. `outbound` for a reply your side sent, so SlyOS’s copy of the conversation is complete.",
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          }
        },
        "required": [
          "channel_id",
          "external_thread_id",
          "external_message_id",
          "body"
        ]
      },
      "ApiIngestMessageResponse": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "SlyOS’s id for the message. Ask for a draft with it."
          },
          "thread_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "SlyOS’s id for the conversation."
          },
          "card_id": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The reply card this message opened."
              },
              {
                "type": "null"
              }
            ],
            "description": "The reply card this message opened, or null when it needs no reply."
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when this `external_message_id` was already stored on this channel. Nothing new was written."
          },
          "needs_reply": {
            "type": "boolean",
            "description": "False when SlyOS decided nobody is waiting for an answer — a receipt, a newsletter. Do not ask for a draft then."
          },
          "bulk": {
            "type": "boolean",
            "description": "True when the message was recognised as bulk mail. It is stored and shown, with no card."
          }
        },
        "required": [
          "message_id",
          "thread_id",
          "card_id",
          "duplicate",
          "needs_reply",
          "bulk"
        ]
      },
      "ApiInsufficientFunds": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "const": "insufficient_funds",
            "description": "Always `insufficient_funds`."
          },
          "message": {
            "type": "string",
            "description": "One sentence, written for a person."
          },
          "available_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "What the wallet could spend when the request arrived."
          },
          "needed_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "What the reply would have cost."
          }
        },
        "required": [
          "code",
          "message",
          "available_cents",
          "needed_cents"
        ]
      },
      "ApiKnowledgePassagesResponse": {
        "type": "object",
        "properties": {
          "source_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The source the passages belong to."
          },
          "passages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "The passage’s id: a reply’s `sources[].passage_id`."
                },
                "ordinal": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Its place in the source, from 0."
                },
                "page": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The page of the file its own text starts on, if the file has pages."
                },
                "page_end": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The last page it runs onto, when it runs past `page`. Null when it stays on one page, the file has no pages, or it was read before page ranges were recorded."
                },
                "section": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The heading it sits under, if any."
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The page of a site it was read from."
                },
                "text": {
                  "type": "string",
                  "description": "The passage, exactly as replies are given it."
                }
              },
              "required": [
                "id",
                "ordinal",
                "page",
                "page_end",
                "section",
                "url",
                "text"
              ]
            },
            "description": "The passages, in order."
          },
          "next_after": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass as `after` for the next page. Null on the last page."
          }
        },
        "required": [
          "source_id",
          "passages",
          "next_after"
        ]
      },
      "ApiKnowledgeSource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The source’s id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "link",
              "site",
              "file"
            ],
            "description": "`link`: one page. `site`: a whole site, read from one address. `file`: an upload."
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The address it is read from. Null for a file."
          },
          "filename": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The uploaded file’s name. Null for a link or a site."
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What it is called on the Developer screen and in a reply’s `sources`."
          },
          "rules": {
            "type": "boolean",
            "description": "True when replies must follow it; false when it is reference material."
          },
          "scope": {
            "type": "string",
            "enum": [
              "project",
              "account"
            ],
            "description": "`project`: replies asked for with this key’s project use it. `account`: replies in every project on the account do."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "ready",
              "failed"
            ],
            "description": "`queued`, then `processing`, while it is read in the background; `ready` once replies use it; `failed` when it could not be read, with `status_reason` saying why."
          },
          "status_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "In a sentence, why the status is what it is. Null when there is nothing to say."
          },
          "warning": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set when the source is `ready` but some of what it shows may be missing, in a sentence naming the pages and what to do — today, pages that build their content with JavaScript, which SlyOS does not run (a price list drawn from a script, a template filled in after the page loads). Null when there is nothing to warn about."
          },
          "incomplete_pages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The addresses of the pages `warning` names: pages whose content is built with JavaScript, so replies may be missing what a visitor sees there. Add that text as a file, or as a single link to a page that shows it. Empty when there are none."
          },
          "pages": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Pages read from it."
          },
          "passages": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Passages it was cut into: what a reply retrieves from."
          },
          "characters": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Characters of text read from it."
          },
          "size_bytes": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "The uploaded file’s size. Null for a link or a site."
          },
          "content_sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "SHA-256 of the text read from it; it changes when a refresh reads something new."
          },
          "file_sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "SHA-256 of the uploaded file’s bytes. Null for a link or a site."
          },
          "used_in_replies": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "How many replies have relied on a passage of it."
          },
          "rules_fit": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "For a rules source: true when every one of the business’s rules reaches every reply; false when they no longer fit in one reply, and each reply gets the rules nearest its message. Null for reference material, or when it was not measured."
          },
          "rules_note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When `rules_fit` is false, in a sentence: how much the rules come to, what one reply carries, and what to do about it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "description": "When it was added."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "description": "When it last changed."
          },
          "fetched_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                "description": "When it was last read. Null until it has been."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "kind",
          "url",
          "filename",
          "title",
          "rules",
          "scope",
          "status",
          "status_reason",
          "warning",
          "incomplete_pages",
          "pages",
          "passages",
          "characters",
          "size_bytes",
          "content_sha256",
          "file_sha256",
          "used_in_replies",
          "rules_fit",
          "rules_note",
          "created_at",
          "updated_at",
          "fetched_at"
        ]
      },
      "ApiKnowledgeUploadRequest": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "contentEncoding": "binary",
            "description": "The file — PDF, Word (.docx), plain text, Markdown or CSV, up to the size this operation states. It is read to the end; a long one is cut into passages, not cut short."
          },
          "title": {
            "description": "What to call it on the Developer screen and in a reply’s `sources`.",
            "type": "string"
          }
        },
        "required": [
          "file"
        ]
      },
      "ApiListChannelsResponse": {
        "type": "object",
        "properties": {
          "channels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "The channel’s id: the `channel_id` every message sent to it names."
                },
                "name": {
                  "type": "string",
                  "description": "Its name on every SlyOS screen."
                },
                "system": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The system behind it, if you named one."
                },
                "environment": {
                  "type": "string",
                  "enum": [
                    "sandbox",
                    "live"
                  ],
                  "description": "The environment of the key that made the call: `sandbox` or `live`."
                },
                "capabilities": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "receive",
                      "send",
                      "attachments",
                      "read_receipts",
                      "mark_read",
                      "delivery_receipts",
                      "reactions",
                      "typing",
                      "threading",
                      "groups"
                    ]
                  },
                  "description": "What the adapter declared it can do."
                },
                "paused": {
                  "type": "boolean",
                  "description": "Whether the account owner has paused the channel."
                },
                "auto_draft": {
                  "type": "boolean",
                  "description": "The channel’s automatic-drafting setting, as the owner last saved it."
                },
                "last_inbound_at": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                      "description": "When a message last arrived on it."
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "last_outbound_at": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                      "description": "When a delivery was last reported on it."
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "system",
                "environment",
                "capabilities",
                "paused",
                "auto_draft",
                "last_inbound_at",
                "last_outbound_at"
              ]
            },
            "description": "Every custom channel in the key’s environment."
          },
          "known_capabilities": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "receive",
                "send",
                "attachments",
                "read_receipts",
                "mark_read",
                "delivery_receipts",
                "reactions",
                "typing",
                "threading",
                "groups"
              ]
            },
            "description": "Every capability an adapter may declare."
          }
        },
        "required": [
          "channels",
          "known_capabilities"
        ]
      },
      "ApiListKnowledgeResponse": {
        "type": "object",
        "properties": {
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "The source’s id."
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "link",
                    "site",
                    "file"
                  ],
                  "description": "`link`: one page. `site`: a whole site, read from one address. `file`: an upload."
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The address it is read from. Null for a file."
                },
                "filename": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The uploaded file’s name. Null for a link or a site."
                },
                "title": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "What it is called on the Developer screen and in a reply’s `sources`."
                },
                "rules": {
                  "type": "boolean",
                  "description": "True when replies must follow it; false when it is reference material."
                },
                "scope": {
                  "type": "string",
                  "enum": [
                    "project",
                    "account"
                  ],
                  "description": "`project`: replies asked for with this key’s project use it. `account`: replies in every project on the account do."
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "queued",
                    "processing",
                    "ready",
                    "failed"
                  ],
                  "description": "`queued`, then `processing`, while it is read in the background; `ready` once replies use it; `failed` when it could not be read, with `status_reason` saying why."
                },
                "status_reason": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "In a sentence, why the status is what it is. Null when there is nothing to say."
                },
                "warning": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Set when the source is `ready` but some of what it shows may be missing, in a sentence naming the pages and what to do — today, pages that build their content with JavaScript, which SlyOS does not run (a price list drawn from a script, a template filled in after the page loads). Null when there is nothing to warn about."
                },
                "incomplete_pages": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "The addresses of the pages `warning` names: pages whose content is built with JavaScript, so replies may be missing what a visitor sees there. Add that text as a file, or as a single link to a page that shows it. Empty when there are none."
                },
                "pages": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Pages read from it."
                },
                "passages": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Passages it was cut into: what a reply retrieves from."
                },
                "characters": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Characters of text read from it."
                },
                "size_bytes": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The uploaded file’s size. Null for a link or a site."
                },
                "content_sha256": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "SHA-256 of the text read from it; it changes when a refresh reads something new."
                },
                "file_sha256": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "SHA-256 of the uploaded file’s bytes. Null for a link or a site."
                },
                "used_in_replies": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "How many replies have relied on a passage of it."
                },
                "rules_fit": {
                  "anyOf": [
                    {
                      "type": "boolean"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "For a rules source: true when every one of the business’s rules reaches every reply; false when they no longer fit in one reply, and each reply gets the rules nearest its message. Null for reference material, or when it was not measured."
                },
                "rules_note": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "When `rules_fit` is false, in a sentence: how much the rules come to, what one reply carries, and what to do about it."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                  "description": "When it was added."
                },
                "updated_at": {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                  "description": "When it last changed."
                },
                "fetched_at": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                      "description": "When it was last read. Null until it has been."
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "required": [
                "id",
                "kind",
                "url",
                "filename",
                "title",
                "rules",
                "scope",
                "status",
                "status_reason",
                "warning",
                "incomplete_pages",
                "pages",
                "passages",
                "characters",
                "size_bytes",
                "content_sha256",
                "file_sha256",
                "used_in_replies",
                "rules_fit",
                "rules_note",
                "created_at",
                "updated_at",
                "fetched_at"
              ]
            },
            "description": "Every source this key’s replies use: its project’s and the account’s."
          }
        },
        "required": [
          "sources"
        ]
      },
      "ApiReadDraftResponse": {
        "type": "object",
        "properties": {
          "draft_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The draft’s id."
          },
          "card_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The card the reply is for."
          },
          "body": {
            "type": "string",
            "description": "The reply text."
          },
          "generation": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "Which draft this is on the card."
          },
          "model": {
            "type": "string",
            "description": "The model that wrote it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "description": "When it was written."
          },
          "held_for_review": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "True when SlyOS’s checks held this reply for a person — money, health or dosing, a legal or data-protection matter, another sensitive subject, or something it gives out that the account has not sent before. Never send a held reply automatically: show it to a person first. Null when SlyOS can no longer say, because the conversation has had a newer reply since; null means held. Send automatically only on `false`."
          },
          "hold_reasons": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "Which check held it: `sensitive` (money, legal, health or credentials), `attachment` (it carries a file), `group` (it goes to a group), `regex_floor` (it matches a rule you set), `outbound_screen` (it gives out something not sent before, or answers a risky request). A code not listed here is still a hold — treat every code you do not recognise as one."
                },
                "label": {
                  "type": "string",
                  "description": "The same reason in words, as the SlyOS inbox shows it."
                }
              },
              "required": [
                "code",
                "label"
              ]
            },
            "description": "Why it was held: one entry per check that held it. Empty when `held_for_review` is not `true`."
          },
          "hold_detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the checks found, in sentences, when the reply was held. Null when it was not held, or when SlyOS cannot say."
          },
          "superseded": {
            "type": "boolean",
            "description": "True when a newer reply has since been written on the same card. Send the newest, not this one."
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                      "description": "The knowledge source the passage came from. Null once that source is deleted."
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "passage_id": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                      "description": "The passage itself. Null once its source is deleted."
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "title": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "What the source was called when the reply was written: its title, file name or address."
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The page the passage was read from, for a link or a site."
                },
                "page": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The page of the file its own text starts on, for a file that has pages. With `page_end`, the range it covers."
                },
                "page_end": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The last page it runs onto, when it runs past `page` — a passage starting on page 11 and carrying page 12 is `page: 11, page_end: 12` (p.11–12). Null when it stays on one page, or the file has no pages."
                },
                "section": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The heading it sat under, if any."
                },
                "rule": {
                  "type": "boolean",
                  "description": "True for a binding rule the reply had to follow; false for reference material it could draw on."
                },
                "rank": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Its place in what the reply was given, from 1 — counted separately for rules and for reference."
                },
                "score": {
                  "anyOf": [
                    {
                      "type": "number"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How close the passage was to the message, as cosine similarity. Null when there was nothing to compare."
                },
                "excerpt": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Up to 280 characters of the passage’s OWN text — past the lines it repeats from the passage before it — from the sentence that shares the most words with the reply, else from its start; `…` marks a cut. While its source still exists."
                },
                "text_sha256": {
                  "type": "string",
                  "description": "SHA-256 of the stored passage the reply was given — all of it, or its first `carried_chars` characters: proof of which text it relied on, even after the source changes."
                },
                "carried_chars": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How many of the passage’s characters the reply was given, when it was cut to fit. Null when it was given the whole passage."
                }
              },
              "required": [
                "source_id",
                "passage_id",
                "title",
                "url",
                "page",
                "page_end",
                "section",
                "rule",
                "rank",
                "score",
                "excerpt",
                "text_sha256",
                "carried_chars"
              ]
            },
            "description": "What this reply relied on: every passage of the business’s own knowledge — the links, sites and files it handed SlyOS, and its rules — that the reply was given, rules first. Empty when the reply was given none: the business has handed SlyOS nothing yet, or nothing it handed over matched."
          }
        },
        "required": [
          "draft_id",
          "card_id",
          "body",
          "generation",
          "model",
          "created_at",
          "held_for_review",
          "hold_reasons",
          "hold_detail",
          "superseded",
          "sources"
        ]
      },
      "ApiRefusal": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "A stable code to branch on."
          },
          "message": {
            "type": "string",
            "description": "One sentence, written for a person."
          }
        },
        "required": [
          "code",
          "message"
        ]
      },
      "ApiSandboxDailyLimit": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "const": "sandbox_daily_limit",
            "description": "Always `sandbox_daily_limit`."
          },
          "message": {
            "type": "string",
            "description": "One sentence, written for a person."
          },
          "used_today": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Sandbox drafts this account had in the last 24 hours."
          },
          "daily_limit": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "What sandbox allows in any 24 hours."
          }
        },
        "required": [
          "code",
          "message",
          "used_today",
          "daily_limit"
        ]
      },
      "ApiSaveBusinessProfileRequest": {
        "type": "object",
        "properties": {
          "business_name": {
            "description": "The business’s name, as replies should give it.",
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "description": "What the business is and does, in your own words: what it sells, who it serves, what it will and will not do.",
            "anyOf": [
              {
                "type": "string",
                "maxLength": 8000
              },
              {
                "type": "null"
              }
            ]
          },
          "tone_notes": {
            "description": "How replies should sound — formal or warm, how to sign off — on top of the voice SlyOS learns from your own replies.",
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ]
          },
          "use_personal_context": {
            "description": "Accepted only when it equals the stored value, so the object `GET` returns can be sent back. A key that tries to change it, either way, is refused: only the account owner can, on the Developer screen.",
            "type": "boolean"
          },
          "updated_at": {
            "description": "Accepted and ignored, so the object `GET` returns can be sent back.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": false
      },
      "ApiUpdateKnowledgeRequest": {
        "type": "object",
        "properties": {
          "rules": {
            "description": "True to make it a binding rule; false to make it reference material.",
            "type": "boolean"
          },
          "title": {
            "description": "A new name for it; null to go back to the name it was read with.",
            "anyOf": [
              {
                "type": "string",
                "maxLength": 300
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "ApiUsageResponse": {
        "type": "object",
        "properties": {
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "The key’s environment, and the only one counted: a live key reads its project’s live usage, a sandbox key its sandbox drafts at nought cents."
          },
          "window_days": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "How many days back the totals cover."
          },
          "responses": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Billable responses charged in the window. Sandbox responses are never billable."
          },
          "charged_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "What those responses cost, in total."
          },
          "price": {
            "type": "object",
            "properties": {
              "version": {
                "type": "string",
                "description": "Today’s price version."
              },
              "cents_per_response": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991,
                "description": "Today’s price for one successful response."
              }
            },
            "required": [
              "version",
              "cents_per_response"
            ],
            "description": "The price in force now."
          }
        },
        "required": [
          "environment",
          "window_days",
          "responses",
          "charged_cents",
          "price"
        ]
      },
      "ApiWalletResponse": {
        "type": "object",
        "properties": {
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "The key’s environment. A sandbox key reads no money — every amount is 0 — and the drafts sandbox still allows today."
          },
          "balance_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Money in the wallet. Always 0 for a sandbox key."
          },
          "reserved_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Held for replies being written right now."
          },
          "available_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "What can be spent: balance minus reserved."
          },
          "responses_remaining": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "How many more replies the available balance pays for at today’s price. For a sandbox key, how many sandbox drafts remain in the last 24 hours’ allowance."
          },
          "sandbox_daily_limit": {
            "description": "Sandbox keys only: the drafts sandbox allows in any 24 hours (50).",
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "environment",
          "balance_cents",
          "reserved_cents",
          "available_cents",
          "responses_remaining"
        ]
      },
      "AssembleVideoRequest": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "Your idempotency id, a UUID. The same one twice is one assembly: a retry never spends a second video."
          },
          "notes": {
            "type": "string",
            "maxLength": 1000,
            "description": "Your words for the edit — \"shorter\", \"open with the price\", \"use take 3 for the ending\". Sent to the editor as written."
          },
          "versions": {
            "anyOf": [
              {
                "type": "number",
                "const": 1
              },
              {
                "type": "number",
                "const": 3
              }
            ],
            "description": "1 asks for one cut where the plan makes three. Never more than the plan’s."
          },
          "take_ids": {
            "minItems": 1,
            "maxItems": 30,
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            },
            "description": "Only these takes. Absent: every ready take."
          },
          "from_render_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "For a change: the cut it starts from — the version being looked at. Absent: the chosen cut, else the newest."
          },
          "target_seconds": {
            "type": "integer",
            "minimum": 10,
            "maximum": 180,
            "description": "The length to aim for this time, 10–180 seconds."
          },
          "appearance": {
            "type": "object",
            "properties": {
              "caption_color": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "white",
                      "yellow",
                      "orange",
                      "pink",
                      "green",
                      "blue",
                      "brand_orange"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The caption words: `white`, `yellow`, `orange`, `pink`, `green`, `blue` or `brand_orange`. null: the brand kit’s text colour again."
              },
              "caption_highlight": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "white",
                      "yellow",
                      "orange",
                      "pink",
                      "green",
                      "blue",
                      "brand_orange"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The word being said, in a pop caption: the same colours. null: the brand kit’s highlight again."
              },
              "caption_font": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "clean",
                      "bold",
                      "classic",
                      "marker"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "`clean` (a sans), `bold` (a heavy display sans), `classic` (a serif) or `marker` (handwritten). null: the brand kit’s font again."
              },
              "speed": {
                "type": "number",
                "minimum": 0.75,
                "maximum": 1.15,
                "description": "The whole video’s speed, 0.75 to 1.15; the voice keeps its pitch and the captions keep time. 1 is as recorded. 1.15 is the fastest a video plays smoothly, so a faster one is refused `bad_request`, never made at another speed."
              },
              "look": {
                "type": "string",
                "enum": [
                  "none",
                  "warm",
                  "cool",
                  "vivid",
                  "mono",
                  "film"
                ],
                "description": "A colour look over the picture (never the captions or the logo): `none`, `warm`, `cool`, `vivid`, `mono` (black and white) or `film`."
              },
              "background": {
                "type": "string",
                "enum": [
                  "blur",
                  "none"
                ],
                "description": "`blur`: everything behind the person softly blurred, the person sharp — what every new video is made with unless asked otherwise. `none`: the background as filmed. A take with nobody in it is never blurred."
              }
            },
            "additionalProperties": false,
            "description": "How it looks and moves, set exactly rather than by the editor. A field left out keeps what the cut it changes has. With no `notes`, a change of only these re-renders that cut’s own clips."
          }
        },
        "required": [
          "request_id"
        ],
        "additionalProperties": false
      },
      "CompleteVideoTakeRequest": {
        "type": "object",
        "properties": {
          "parts": {
            "minItems": 1,
            "maxItems": 10000,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "part_number": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10000,
                  "description": "The part (1-based)."
                },
                "etag": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "The `ETag` header of that part’s PUT answer, as sent."
                }
              },
              "required": [
                "part_number",
                "etag"
              ],
              "additionalProperties": false
            },
            "description": "Every part, each with the ETag storage answered its PUT with."
          }
        },
        "required": [
          "parts"
        ],
        "additionalProperties": false
      },
      "DraftReadyEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The event’s own id. A retry of the same event carries the same id: deduplicate on it."
          },
          "type": {
            "type": "string",
            "const": "draft.ready",
            "description": "The event type."
          },
          "created": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "When the event happened, in Unix seconds — the same on every retry, so an old event reads as old."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "Which of the account’s two endpoints it was sent to: `sandbox` or `live`."
          },
          "data": {
            "type": "object",
            "properties": {
              "draft_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The draft that is ready. Read it with `GET /v1/developer/drafts/{draftId}`."
              },
              "card_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The card it is for."
              },
              "generation_id": {
                "type": "string",
                "description": "The `generation_id` you asked for it with."
              },
              "generation": {
                "type": "integer",
                "exclusiveMinimum": 0,
                "maximum": 9007199254740991,
                "description": "Which draft this is on the card: 1 for the first, higher after another."
              },
              "environment": {
                "type": "string",
                "enum": [
                  "sandbox",
                  "live"
                ],
                "description": "The environment of the key that made the call: `sandbox` or `live`."
              },
              "held_for_review": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "True when SlyOS’s checks held this reply for a person — money, health or dosing, a legal or data-protection matter, another sensitive subject, or something it gives out that the account has not sent before. Never send a held reply automatically: show it to a person first. Null when SlyOS can no longer say, because the conversation has had a newer reply since; null means held. Send automatically only on `false`."
              },
              "hold_reasons": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "description": "Which check held it: `sensitive` (money, legal, health or credentials), `attachment` (it carries a file), `group` (it goes to a group), `regex_floor` (it matches a rule you set), `outbound_screen` (it gives out something not sent before, or answers a risky request). A code not listed here is still a hold — treat every code you do not recognise as one."
                    },
                    "label": {
                      "type": "string",
                      "description": "The same reason in words, as the SlyOS inbox shows it."
                    }
                  },
                  "required": [
                    "code",
                    "label"
                  ]
                },
                "description": "Why it was held: one entry per check that held it. Empty when `held_for_review` is not `true`."
              },
              "hold_detail": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "What the checks found, in sentences, when the reply was held. Null when it was not held, or when SlyOS cannot say."
              }
            },
            "required": [
              "draft_id",
              "card_id",
              "generation_id",
              "generation",
              "environment",
              "held_for_review",
              "hold_reasons",
              "hold_detail"
            ],
            "description": "What is ready."
          }
        },
        "required": [
          "id",
          "type",
          "created",
          "environment",
          "data"
        ]
      },
      "EvaluateVideoRequest": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "Your idempotency id, a UUID. The same one twice is one check, spent once."
          },
          "subject": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "take_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "A take of this video — a `check` video’s only take."
                  }
                },
                "required": [
                  "take_id"
                ],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "render_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "A cut of this video."
                  }
                },
                "required": [
                  "render_id"
                ],
                "additionalProperties": false
              }
            ],
            "description": "What to check: one of the video’s takes, or a cut SlyOS made."
          },
          "links": {
            "maxItems": 5,
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 2000
            },
            "description": "Up to 5 pages to judge it against, beside About you."
          },
          "use_about_you": {
            "default": true,
            "type": "boolean",
            "description": "Judge it against the business’s About you too. Default true."
          },
          "focus": {
            "type": "string",
            "maxLength": 500,
            "description": "What you want to know, in your words: \"is the pricing right?\""
          }
        },
        "required": [
          "request_id",
          "subject"
        ],
        "additionalProperties": false
      },
      "EvaluateVideoResponse": {
        "type": "object",
        "properties": {
          "evaluation": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The check’s id."
              },
              "project_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The video it belongs to."
              },
              "subject": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "take_id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "The take checked."
                      }
                    },
                    "required": [
                      "take_id"
                    ]
                  },
                  {
                    "type": "object",
                    "properties": {
                      "render_id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "The cut checked."
                      }
                    },
                    "required": [
                      "render_id"
                    ]
                  }
                ],
                "description": "What was checked: one of the video’s takes, or a cut SlyOS made."
              },
              "state": {
                "type": "string",
                "enum": [
                  "checking",
                  "ready",
                  "failed"
                ],
                "description": "`checking`, `ready` or `failed`."
              },
              "body": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "verdict": {
                        "type": "string",
                        "enum": [
                          "strong",
                          "good",
                          "needs_work"
                        ],
                        "description": "`strong` (ready to post), `good` (good, with a few fixes) or `needs_work` (worth another take)."
                      },
                      "summary": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 600,
                        "description": "The feedback in a few sentences."
                      },
                      "hook": {
                        "type": "object",
                        "properties": {
                          "works": {
                            "type": "boolean",
                            "description": "Whether the first seconds would keep a viewer."
                          },
                          "why": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 300,
                            "description": "Why, in a sentence."
                          },
                          "instead": {
                            "anyOf": [
                              {
                                "type": "string",
                                "maxLength": 300
                              },
                              {
                                "type": "null"
                              }
                            ],
                            "description": "A better opening line, or null."
                          }
                        },
                        "required": [
                          "works",
                          "why",
                          "instead"
                        ],
                        "description": "Whether the opening works, why, and what to say instead."
                      },
                      "strengths": {
                        "maxItems": 5,
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "point": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 400,
                              "description": "What works, in a sentence."
                            },
                            "at_seconds": {
                              "anyOf": [
                                {
                                  "type": "number",
                                  "minimum": 0
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "Where in the video, in seconds. Null for the whole of it."
                            },
                            "cites": {
                              "maxItems": 5,
                              "type": "array",
                              "items": {
                                "type": "integer",
                                "exclusiveMinimum": 0,
                                "maximum": 9007199254740991
                              },
                              "description": "The references it stands on, by `n`."
                            }
                          },
                          "required": [
                            "point",
                            "at_seconds",
                            "cites"
                          ]
                        },
                        "description": "What works, at most five."
                      },
                      "fixes": {
                        "maxItems": 8,
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "point": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 400,
                              "description": "What to change, in a sentence."
                            },
                            "at_seconds": {
                              "anyOf": [
                                {
                                  "type": "number",
                                  "minimum": 0
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "Where in the video, in seconds. Null for the whole of it."
                            },
                            "cites": {
                              "maxItems": 5,
                              "type": "array",
                              "items": {
                                "type": "integer",
                                "exclusiveMinimum": 0,
                                "maximum": 9007199254740991
                              },
                              "description": "The references it stands on, by `n`."
                            },
                            "weight": {
                              "type": "string",
                              "enum": [
                                "must",
                                "should",
                                "could"
                              ],
                              "description": "`must` (wrong or off-brand), `should` (weaker than it could be) or `could` (polish)."
                            },
                            "instead": {
                              "anyOf": [
                                {
                                  "type": "string",
                                  "maxLength": 400
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "What to say or do instead, when there is something concrete. Else null."
                            }
                          },
                          "required": [
                            "point",
                            "at_seconds",
                            "cites",
                            "weight",
                            "instead"
                          ]
                        },
                        "description": "What to change, at most eight, most important first."
                      },
                      "claims": {
                        "maxItems": 10,
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "claim": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 300,
                              "description": "The fact, as the video states it."
                            },
                            "at_seconds": {
                              "anyOf": [
                                {
                                  "type": "number",
                                  "minimum": 0
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "Where in the video, in seconds. Null when it is said more than once."
                            },
                            "status": {
                              "type": "string",
                              "enum": [
                                "supported",
                                "contradicted",
                                "not_found"
                              ],
                              "description": "`supported`, `contradicted`, or `not_found` — nothing given to SlyOS says it, which is not the same as wrong."
                            },
                            "cites": {
                              "maxItems": 5,
                              "type": "array",
                              "items": {
                                "type": "integer",
                                "exclusiveMinimum": 0,
                                "maximum": 9007199254740991
                              },
                              "description": "The references it was checked against, by `n`."
                            }
                          },
                          "required": [
                            "claim",
                            "at_seconds",
                            "status",
                            "cites"
                          ]
                        },
                        "description": "Every fact the video states, checked against About you and the links: supported, contradicted, or not found."
                      },
                      "references": {
                        "maxItems": 15,
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "n": {
                              "type": "integer",
                              "exclusiveMinimum": 0,
                              "maximum": 9007199254740991,
                              "description": "Its number, as `cites` names it."
                            },
                            "kind": {
                              "type": "string",
                              "enum": [
                                "about_rules",
                                "about_document",
                                "about_link",
                                "link"
                              ],
                              "description": "`about_rules`, `about_document` or `about_link` (from About you), or `link` (a link sent with the check)."
                            },
                            "label": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 200,
                              "description": "What it is, in a few words."
                            },
                            "source_id": {
                              "anyOf": [
                                {
                                  "type": "string",
                                  "format": "uuid",
                                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "The About-you source, for an About-you reference. Else null."
                            },
                            "url": {
                              "anyOf": [
                                {
                                  "type": "string",
                                  "format": "uri"
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "Where it is on the web, when it is a page. Else null."
                            },
                            "excerpt": {
                              "type": "string",
                              "maxLength": 400,
                              "description": "The words relied on, quoted."
                            }
                          },
                          "required": [
                            "n",
                            "kind",
                            "label",
                            "source_id",
                            "url",
                            "excerpt"
                          ]
                        },
                        "description": "What the feedback relied on, numbered. Every `cites` names one of these; a citation of anything else is refused before the feedback is stored."
                      }
                    },
                    "required": [
                      "verdict",
                      "summary",
                      "hook",
                      "strengths",
                      "fixes",
                      "claims",
                      "references"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The feedback. Null until `state` is `ready`."
              },
              "failure": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Why it failed, in a sentence. Set exactly when `state` is `failed`."
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                "description": "When it was asked for."
              }
            },
            "required": [
              "id",
              "project_id",
              "subject",
              "state",
              "body",
              "failure",
              "created_at"
            ],
            "description": "The check, `checking`. Read it again until `state` is `ready`, or wait for `video.evaluated`."
          },
          "job": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The job’s id."
              },
              "kind": {
                "type": "string",
                "enum": [
                  "prepare",
                  "assemble",
                  "evaluate"
                ],
                "description": "`prepare` a take, `assemble` the video, or `evaluate` (check) it."
              },
              "state": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "succeeded",
                  "failed",
                  "canceled"
                ],
                "description": "`queued`, `running`, `succeeded`, `failed` or `canceled`."
              },
              "step": {
                "type": "string",
                "enum": [
                  "waiting",
                  "probing",
                  "listening",
                  "framing",
                  "proxying",
                  "choosing",
                  "cutting",
                  "rendering",
                  "finishing",
                  "watching",
                  "reading",
                  "judging"
                ],
                "description": "What the job is doing now. `step_words` says it in words."
              },
              "step_words": {
                "type": "string",
                "description": "The step in plain words — the same words every SlyOS screen shows."
              },
              "progress": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "0–100, from a count SlyOS really has (parts rendered of parts to render). Null when there is none; never an estimate."
              },
              "ahead": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Jobs of other accounts ahead of this one while it waits. Null once it runs."
              },
              "failure": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Why it failed, in a sentence. Set exactly when `state` is `failed`."
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                "description": "When it was queued."
              },
              "finished_at": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When it succeeded, failed or was canceled. Null until then."
              }
            },
            "required": [
              "id",
              "kind",
              "state",
              "step",
              "step_words",
              "progress",
              "ahead",
              "failure",
              "created_at",
              "finished_at"
            ],
            "description": "The job doing the check."
          }
        },
        "required": [
          "evaluation",
          "job"
        ]
      },
      "OpenVideoTakeRequest": {
        "type": "object",
        "properties": {
          "filename": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300,
            "description": "The file’s name, for people. Never part of a storage key."
          },
          "mime_type": {
            "type": "string",
            "enum": [
              "video/mp4",
              "video/quicktime",
              "video/webm"
            ],
            "description": "`video/mp4`, `video/quicktime` or `video/webm`. Checked against the bytes later."
          },
          "size_bytes": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "The file’s exact size in bytes. Completing the take checks it."
          },
          "duration_seconds": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 86400,
            "description": "How long it runs, read on your side. Checked against the plan before any upload, and against SlyOS’s own measure after."
          }
        },
        "required": [
          "filename",
          "mime_type",
          "size_bytes",
          "duration_seconds"
        ],
        "additionalProperties": false
      },
      "OpenVideoTakeResponse": {
        "type": "object",
        "properties": {
          "take": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The take’s id."
              },
              "n": {
                "type": "integer",
                "exclusiveMinimum": 0,
                "maximum": 9007199254740991,
                "description": "Take 1, 2, 3… in the order they were added."
              },
              "filename": {
                "type": "string",
                "description": "The name the file was sent with. Never part of a storage key."
              },
              "state": {
                "type": "string",
                "enum": [
                  "uploading",
                  "uploaded",
                  "preparing",
                  "ready",
                  "failed",
                  "discarded"
                ],
                "description": "`uploading` → `uploaded` → `preparing` → `ready`. Or `failed` (`failure` says why) or `discarded`."
              },
              "seconds": {
                "anyOf": [
                  {
                    "type": "number",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "How long the take runs, as SlyOS measured it. Null until measured."
              },
              "poster_url": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "uri"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "A still from the take, good for an hour. Null until it is prepared."
              },
              "failure": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Why the take cannot be used, in a sentence. Set exactly when `state` is `failed`."
              },
              "note": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Something worth knowing that is not a failure — \"No face found, so it is framed in the middle.\" Null when there is nothing."
              },
              "upload": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "part_bytes": {
                        "type": "integer",
                        "exclusiveMinimum": 0,
                        "maximum": 9007199254740991,
                        "description": "Bytes in every part but the last (16,777,216)."
                      },
                      "part_count": {
                        "type": "integer",
                        "exclusiveMinimum": 0,
                        "maximum": 9007199254740991,
                        "description": "How many parts the file is cut into."
                      },
                      "parts_done": {
                        "anyOf": [
                          {
                            "type": "array",
                            "items": {
                              "type": "integer",
                              "minimum": 1,
                              "maximum": 10000
                            }
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "Part numbers storage already holds — skip them when resuming. Null when SlyOS cannot list them: resume from your own record."
                      }
                    },
                    "required": [
                      "part_bytes",
                      "part_count",
                      "parts_done"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "While `state` is `uploading`: how the file is cut into parts and which parts storage holds. Null otherwise."
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                "description": "When the take was added."
              }
            },
            "required": [
              "id",
              "n",
              "filename",
              "state",
              "seconds",
              "poster_url",
              "failure",
              "note",
              "upload",
              "created_at"
            ],
            "description": "The take, `uploading`."
          },
          "urls": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "part_number": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10000,
                  "description": "Which part to PUT to this address (1-based)."
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "A presigned PUT, good for fifteen minutes. Send the part’s bytes as the body and keep the `ETag` header of the answer: completing the take needs it."
                }
              },
              "required": [
                "part_number",
                "url"
              ]
            },
            "description": "Addresses for the first parts — up to 20. Ask for the rest with `part-urls`."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When these addresses stop working. Ask for fresh ones with `part-urls`."
          }
        },
        "required": [
          "take",
          "urls",
          "expires_at"
        ]
      },
      "VideoDownloadLink": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "A presigned GET that downloads the MP4, good for 10 minutes. Fetch it from your server; do not publish it."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When the link stops working. Ask again for a fresh one."
          },
          "filename": {
            "type": "string",
            "description": "The file name it downloads as."
          },
          "bytes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Its size."
          },
          "mime_type": {
            "type": "string",
            "enum": [
              "video/mp4",
              "image/jpeg"
            ],
            "description": "`video/mp4` here: this route hands out the video. (`image/jpeg` is a cut’s cover, which the SlyOS app’s own route hands out with `?file=cover`.)"
          }
        },
        "required": [
          "url",
          "expires_at",
          "filename",
          "bytes",
          "mime_type"
        ]
      },
      "VideoEvaluatedEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The event’s own id. A retry of the same event carries the same id: deduplicate on it."
          },
          "type": {
            "type": "string",
            "const": "video.evaluated",
            "description": "The event type."
          },
          "created": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "When the event happened, in Unix seconds — the same on every retry, so an old event reads as old."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "Which of the account’s two endpoints it was sent to: `sandbox` or `live`."
          },
          "data": {
            "type": "object",
            "properties": {
              "video_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The video, as `id` on its `VideoProjectView`."
              },
              "evaluation_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The check. Read the feedback with `GET /v1/developer/video-evaluations/{evaluationId}`."
              },
              "verdict": {
                "type": "string",
                "enum": [
                  "strong",
                  "good",
                  "needs_work"
                ],
                "description": "`strong` (ready to post), `good` (good, with a few fixes) or `needs_work` (worth another take)."
              },
              "environment": {
                "type": "string",
                "enum": [
                  "sandbox",
                  "live"
                ],
                "description": "The environment of the key that made the call: `sandbox` or `live`."
              }
            },
            "required": [
              "video_id",
              "evaluation_id",
              "verdict",
              "environment"
            ],
            "description": "Which check is done, and its verdict."
          }
        },
        "required": [
          "id",
          "type",
          "created",
          "environment",
          "data"
        ]
      },
      "VideoEvaluationView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The check’s id."
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The video it belongs to."
          },
          "subject": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "take_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "The take checked."
                  }
                },
                "required": [
                  "take_id"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "render_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "The cut checked."
                  }
                },
                "required": [
                  "render_id"
                ]
              }
            ],
            "description": "What was checked: one of the video’s takes, or a cut SlyOS made."
          },
          "state": {
            "type": "string",
            "enum": [
              "checking",
              "ready",
              "failed"
            ],
            "description": "`checking`, `ready` or `failed`."
          },
          "body": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "verdict": {
                    "type": "string",
                    "enum": [
                      "strong",
                      "good",
                      "needs_work"
                    ],
                    "description": "`strong` (ready to post), `good` (good, with a few fixes) or `needs_work` (worth another take)."
                  },
                  "summary": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 600,
                    "description": "The feedback in a few sentences."
                  },
                  "hook": {
                    "type": "object",
                    "properties": {
                      "works": {
                        "type": "boolean",
                        "description": "Whether the first seconds would keep a viewer."
                      },
                      "why": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 300,
                        "description": "Why, in a sentence."
                      },
                      "instead": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 300
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "A better opening line, or null."
                      }
                    },
                    "required": [
                      "works",
                      "why",
                      "instead"
                    ],
                    "description": "Whether the opening works, why, and what to say instead."
                  },
                  "strengths": {
                    "maxItems": 5,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "point": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 400,
                          "description": "What works, in a sentence."
                        },
                        "at_seconds": {
                          "anyOf": [
                            {
                              "type": "number",
                              "minimum": 0
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Where in the video, in seconds. Null for the whole of it."
                        },
                        "cites": {
                          "maxItems": 5,
                          "type": "array",
                          "items": {
                            "type": "integer",
                            "exclusiveMinimum": 0,
                            "maximum": 9007199254740991
                          },
                          "description": "The references it stands on, by `n`."
                        }
                      },
                      "required": [
                        "point",
                        "at_seconds",
                        "cites"
                      ]
                    },
                    "description": "What works, at most five."
                  },
                  "fixes": {
                    "maxItems": 8,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "point": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 400,
                          "description": "What to change, in a sentence."
                        },
                        "at_seconds": {
                          "anyOf": [
                            {
                              "type": "number",
                              "minimum": 0
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Where in the video, in seconds. Null for the whole of it."
                        },
                        "cites": {
                          "maxItems": 5,
                          "type": "array",
                          "items": {
                            "type": "integer",
                            "exclusiveMinimum": 0,
                            "maximum": 9007199254740991
                          },
                          "description": "The references it stands on, by `n`."
                        },
                        "weight": {
                          "type": "string",
                          "enum": [
                            "must",
                            "should",
                            "could"
                          ],
                          "description": "`must` (wrong or off-brand), `should` (weaker than it could be) or `could` (polish)."
                        },
                        "instead": {
                          "anyOf": [
                            {
                              "type": "string",
                              "maxLength": 400
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "What to say or do instead, when there is something concrete. Else null."
                        }
                      },
                      "required": [
                        "point",
                        "at_seconds",
                        "cites",
                        "weight",
                        "instead"
                      ]
                    },
                    "description": "What to change, at most eight, most important first."
                  },
                  "claims": {
                    "maxItems": 10,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "claim": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 300,
                          "description": "The fact, as the video states it."
                        },
                        "at_seconds": {
                          "anyOf": [
                            {
                              "type": "number",
                              "minimum": 0
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Where in the video, in seconds. Null when it is said more than once."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "supported",
                            "contradicted",
                            "not_found"
                          ],
                          "description": "`supported`, `contradicted`, or `not_found` — nothing given to SlyOS says it, which is not the same as wrong."
                        },
                        "cites": {
                          "maxItems": 5,
                          "type": "array",
                          "items": {
                            "type": "integer",
                            "exclusiveMinimum": 0,
                            "maximum": 9007199254740991
                          },
                          "description": "The references it was checked against, by `n`."
                        }
                      },
                      "required": [
                        "claim",
                        "at_seconds",
                        "status",
                        "cites"
                      ]
                    },
                    "description": "Every fact the video states, checked against About you and the links: supported, contradicted, or not found."
                  },
                  "references": {
                    "maxItems": 15,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "n": {
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "maximum": 9007199254740991,
                          "description": "Its number, as `cites` names it."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "about_rules",
                            "about_document",
                            "about_link",
                            "link"
                          ],
                          "description": "`about_rules`, `about_document` or `about_link` (from About you), or `link` (a link sent with the check)."
                        },
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 200,
                          "description": "What it is, in a few words."
                        },
                        "source_id": {
                          "anyOf": [
                            {
                              "type": "string",
                              "format": "uuid",
                              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "The About-you source, for an About-you reference. Else null."
                        },
                        "url": {
                          "anyOf": [
                            {
                              "type": "string",
                              "format": "uri"
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Where it is on the web, when it is a page. Else null."
                        },
                        "excerpt": {
                          "type": "string",
                          "maxLength": 400,
                          "description": "The words relied on, quoted."
                        }
                      },
                      "required": [
                        "n",
                        "kind",
                        "label",
                        "source_id",
                        "url",
                        "excerpt"
                      ]
                    },
                    "description": "What the feedback relied on, numbered. Every `cites` names one of these; a citation of anything else is refused before the feedback is stored."
                  }
                },
                "required": [
                  "verdict",
                  "summary",
                  "hook",
                  "strengths",
                  "fixes",
                  "claims",
                  "references"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The feedback. Null until `state` is `ready`."
          },
          "failure": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why it failed, in a sentence. Set exactly when `state` is `failed`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When it was asked for."
          }
        },
        "required": [
          "id",
          "project_id",
          "subject",
          "state",
          "body",
          "failure",
          "created_at"
        ]
      },
      "VideoFailedEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The event’s own id. A retry of the same event carries the same id: deduplicate on it."
          },
          "type": {
            "type": "string",
            "const": "video.failed",
            "description": "The event type."
          },
          "created": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "When the event happened, in Unix seconds — the same on every retry, so an old event reads as old."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "Which of the account’s two endpoints it was sent to: `sandbox` or `live`."
          },
          "data": {
            "type": "object",
            "properties": {
              "video_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The video, as `id` on its `VideoProjectView`."
              },
              "job_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The assembly that failed."
              },
              "failure": {
                "type": "string",
                "description": "Why, in a sentence written for a person. The video it would have spent is given back."
              },
              "environment": {
                "type": "string",
                "enum": [
                  "sandbox",
                  "live"
                ],
                "description": "The environment of the key that made the call: `sandbox` or `live`."
              }
            },
            "required": [
              "video_id",
              "job_id",
              "failure",
              "environment"
            ],
            "description": "Which video, and why it could not be made."
          }
        },
        "required": [
          "id",
          "type",
          "created",
          "environment",
          "data"
        ]
      },
      "VideoJobResponse": {
        "type": "object",
        "properties": {
          "job": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The job’s id."
              },
              "kind": {
                "type": "string",
                "enum": [
                  "prepare",
                  "assemble",
                  "evaluate"
                ],
                "description": "`prepare` a take, `assemble` the video, or `evaluate` (check) it."
              },
              "state": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "succeeded",
                  "failed",
                  "canceled"
                ],
                "description": "`queued`, `running`, `succeeded`, `failed` or `canceled`."
              },
              "step": {
                "type": "string",
                "enum": [
                  "waiting",
                  "probing",
                  "listening",
                  "framing",
                  "proxying",
                  "choosing",
                  "cutting",
                  "rendering",
                  "finishing",
                  "watching",
                  "reading",
                  "judging"
                ],
                "description": "What the job is doing now. `step_words` says it in words."
              },
              "step_words": {
                "type": "string",
                "description": "The step in plain words — the same words every SlyOS screen shows."
              },
              "progress": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "0–100, from a count SlyOS really has (parts rendered of parts to render). Null when there is none; never an estimate."
              },
              "ahead": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Jobs of other accounts ahead of this one while it waits. Null once it runs."
              },
              "failure": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Why it failed, in a sentence. Set exactly when `state` is `failed`."
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                "description": "When it was queued."
              },
              "finished_at": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When it succeeded, failed or was canceled. Null until then."
              }
            },
            "required": [
              "id",
              "kind",
              "state",
              "step",
              "step_words",
              "progress",
              "ahead",
              "failure",
              "created_at",
              "finished_at"
            ],
            "description": "The job, queued."
          }
        },
        "required": [
          "job"
        ]
      },
      "VideoPartUrlsRequest": {
        "type": "object",
        "properties": {
          "part_numbers": {
            "minItems": 1,
            "maxItems": 100,
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            },
            "description": "The parts that need an address, 1 to 100 at a time."
          }
        },
        "required": [
          "part_numbers"
        ],
        "additionalProperties": false
      },
      "VideoPartUrlsResponse": {
        "type": "object",
        "properties": {
          "urls": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "part_number": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10000,
                  "description": "Which part to PUT to this address (1-based)."
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "A presigned PUT, good for fifteen minutes. Send the part’s bytes as the body and keep the `ETag` header of the answer: completing the take needs it."
                }
              },
              "required": [
                "part_number",
                "url"
              ]
            },
            "description": "Upload addresses, one per part."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When these addresses stop working. Ask for fresh ones with `part-urls`."
          }
        },
        "required": [
          "urls",
          "expires_at"
        ]
      },
      "VideoProjectView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The video’s id. Every other video endpoint takes it."
          },
          "kind": {
            "type": "string",
            "enum": [
              "make",
              "check"
            ],
            "description": "`make`: script, takes, finished video. `check`: a video uploaded only to be checked."
          },
          "title": {
            "type": "string",
            "description": "What it is called."
          },
          "state": {
            "type": "string",
            "enum": [
              "script",
              "takes",
              "making",
              "ready",
              "failed"
            ],
            "description": "Where it stands, worked out from its takes, jobs and cuts: `script`, `takes`, `making`, `ready` or `failed`."
          },
          "platform": {
            "type": "string",
            "enum": [
              "tiktok",
              "instagram",
              "both"
            ],
            "description": "`tiktok`, `instagram` or `both`. One 9:16 file suits both."
          },
          "target_seconds": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "The length the cut aims for, in seconds."
          },
          "versions": {
            "anyOf": [
              {
                "type": "number",
                "const": 1
              },
              {
                "type": "number",
                "const": 3
              }
            ],
            "description": "1, or 3 cuts to pick from where the plan makes three."
          },
          "script": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "What the video is called. The downloaded file is named after it."
                  },
                  "lines": {
                    "minItems": 1,
                    "maxItems": 30,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "text": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 400,
                          "description": "What is said."
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "hook",
                            "line",
                            "cta"
                          ],
                          "description": "`hook` opens the video wherever it sits in the script; `cta` closes it; `line` is everything between. At most one hook."
                        },
                        "on_screen": {
                          "anyOf": [
                            {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 60
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Big words shown while the line is said, or null."
                        }
                      },
                      "required": [
                        "text",
                        "role",
                        "on_screen"
                      ]
                    },
                    "description": "The spoken lines, in the order they are written."
                  },
                  "caption": {
                    "type": "string",
                    "maxLength": 2200,
                    "description": "The caption to post with it — up to 2,200 characters, Instagram’s limit."
                  },
                  "hashtags": {
                    "maxItems": 10,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^#[\\p{L}\\p{N}_]{1,49}$"
                    },
                    "description": "Up to 10 hashtags, each `#` and letters, digits or `_`."
                  },
                  "source_ids": {
                    "maxItems": 10,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                    },
                    "description": "The About-you sources the script drew on, by id, so its facts can be traced. Empty when it drew on none."
                  }
                },
                "required": [
                  "title",
                  "lines",
                  "caption",
                  "hashtags",
                  "source_ids"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The script. Null for a `check` video, or before one is written."
          },
          "takes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "The take’s id."
                },
                "n": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Take 1, 2, 3… in the order they were added."
                },
                "filename": {
                  "type": "string",
                  "description": "The name the file was sent with. Never part of a storage key."
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "uploading",
                    "uploaded",
                    "preparing",
                    "ready",
                    "failed",
                    "discarded"
                  ],
                  "description": "`uploading` → `uploaded` → `preparing` → `ready`. Or `failed` (`failure` says why) or `discarded`."
                },
                "seconds": {
                  "anyOf": [
                    {
                      "type": "number",
                      "minimum": 0
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How long the take runs, as SlyOS measured it. Null until measured."
                },
                "poster_url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uri"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "A still from the take, good for an hour. Null until it is prepared."
                },
                "failure": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Why the take cannot be used, in a sentence. Set exactly when `state` is `failed`."
                },
                "note": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Something worth knowing that is not a failure — \"No face found, so it is framed in the middle.\" Null when there is nothing."
                },
                "upload": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "part_bytes": {
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "maximum": 9007199254740991,
                          "description": "Bytes in every part but the last (16,777,216)."
                        },
                        "part_count": {
                          "type": "integer",
                          "exclusiveMinimum": 0,
                          "maximum": 9007199254740991,
                          "description": "How many parts the file is cut into."
                        },
                        "parts_done": {
                          "anyOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 10000
                              }
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Part numbers storage already holds — skip them when resuming. Null when SlyOS cannot list them: resume from your own record."
                        }
                      },
                      "required": [
                        "part_bytes",
                        "part_count",
                        "parts_done"
                      ]
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "While `state` is `uploading`: how the file is cut into parts and which parts storage holds. Null otherwise."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                  "description": "When the take was added."
                }
              },
              "required": [
                "id",
                "n",
                "filename",
                "state",
                "seconds",
                "poster_url",
                "failure",
                "note",
                "upload",
                "created_at"
              ]
            },
            "description": "Every take, in the order added."
          },
          "job": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "The job’s id."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "prepare",
                      "assemble",
                      "evaluate"
                    ],
                    "description": "`prepare` a take, `assemble` the video, or `evaluate` (check) it."
                  },
                  "state": {
                    "type": "string",
                    "enum": [
                      "queued",
                      "running",
                      "succeeded",
                      "failed",
                      "canceled"
                    ],
                    "description": "`queued`, `running`, `succeeded`, `failed` or `canceled`."
                  },
                  "step": {
                    "type": "string",
                    "enum": [
                      "waiting",
                      "probing",
                      "listening",
                      "framing",
                      "proxying",
                      "choosing",
                      "cutting",
                      "rendering",
                      "finishing",
                      "watching",
                      "reading",
                      "judging"
                    ],
                    "description": "What the job is doing now. `step_words` says it in words."
                  },
                  "step_words": {
                    "type": "string",
                    "description": "The step in plain words — the same words every SlyOS screen shows."
                  },
                  "progress": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "0–100, from a count SlyOS really has (parts rendered of parts to render). Null when there is none; never an estimate."
                  },
                  "ahead": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 9007199254740991
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Jobs of other accounts ahead of this one while it waits. Null once it runs."
                  },
                  "failure": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Why it failed, in a sentence. Set exactly when `state` is `failed`."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                    "description": "When it was queued."
                  },
                  "finished_at": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "When it succeeded, failed or was canceled. Null until then."
                  }
                },
                "required": [
                  "id",
                  "kind",
                  "state",
                  "step",
                  "step_words",
                  "progress",
                  "ahead",
                  "failure",
                  "created_at",
                  "finished_at"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The job running or queued now, else the last one. Null when there has been none."
          },
          "renders": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "The cut’s id. Mint a download link for it with the download endpoint."
                },
                "label": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2,
                  "description": "`A`, `B` or `C`: the order the versions are shown in."
                },
                "style": {
                  "type": "string",
                  "enum": [
                    "tight",
                    "story",
                    "punchy"
                  ],
                  "description": "`tight` (the shortest that keeps every point), `story` (keeps the breaths that carry it) or `punchy` (opens on the strongest line, fastest cuts)."
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "rendering",
                    "ready",
                    "failed",
                    "expired"
                  ],
                  "description": "`rendering`, `ready`, `failed`, or `expired` — kept past the plan’s days and its files deleted."
                },
                "seconds": {
                  "anyOf": [
                    {
                      "type": "number",
                      "minimum": 0
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How long it runs. Null until rendered."
                },
                "width": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "exclusiveMinimum": 0,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Pixels across (1080). Null until rendered."
                },
                "height": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "exclusiveMinimum": 0,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Pixels down (1920). Null until rendered."
                },
                "bytes": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The MP4’s size. Null until rendered."
                },
                "poster_url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uri"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "A still to show before it plays, good for an hour. Null until rendered."
                },
                "preview_url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uri"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The cut to PLAY inline, good for an hour. To download it, use the download endpoint. Null until rendered."
                },
                "chosen": {
                  "type": "boolean",
                  "description": "Picked from the versions. The next change starts from it."
                },
                "kept_until": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "When its files are deleted. Download it before then to keep it."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
                  "description": "When the assembly that made it started."
                }
              },
              "required": [
                "id",
                "label",
                "style",
                "state",
                "seconds",
                "width",
                "height",
                "bytes",
                "poster_url",
                "preview_url",
                "chosen",
                "kept_until",
                "created_at"
              ]
            },
            "description": "Finished cuts, newest assembly first; within one, A then B then C."
          },
          "caption": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The caption to post with the chosen cut. Null until written."
          },
          "hashtags": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^#[\\p{L}\\p{N}_]{1,49}$"
            },
            "description": "The hashtags to post with it."
          },
          "changes_left": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Changes left (\"make it shorter\") before the next assembly spends another of the month’s videos."
          },
          "evaluation_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            },
            "description": "Its checks, newest first. Read each with `GET /v1/developer/video-evaluations/{evaluationId}`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When it was started."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When anything about it last changed."
          }
        },
        "required": [
          "id",
          "kind",
          "title",
          "state",
          "platform",
          "target_seconds",
          "versions",
          "script",
          "takes",
          "job",
          "renders",
          "caption",
          "hashtags",
          "changes_left",
          "evaluation_ids",
          "created_at",
          "updated_at"
        ]
      },
      "VideoReadyEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The event’s own id. A retry of the same event carries the same id: deduplicate on it."
          },
          "type": {
            "type": "string",
            "const": "video.ready",
            "description": "The event type."
          },
          "created": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "When the event happened, in Unix seconds — the same on every retry, so an old event reads as old."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "Which of the account’s two endpoints it was sent to: `sandbox` or `live`."
          },
          "data": {
            "type": "object",
            "properties": {
              "video_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                "description": "The video, as `id` on its `VideoProjectView`."
              },
              "render_ids": {
                "minItems": 1,
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "A finished cut."
                },
                "description": "The cuts this assembly made — one, or three to pick from — A first. Mint a download link for each with `GET /v1/developer/videos/{videoId}/renders/{renderId}/download`."
              },
              "environment": {
                "type": "string",
                "enum": [
                  "sandbox",
                  "live"
                ],
                "description": "The environment of the key that made the call: `sandbox` or `live`."
              }
            },
            "required": [
              "video_id",
              "render_ids",
              "environment"
            ],
            "description": "Which video, and its cuts."
          }
        },
        "required": [
          "id",
          "type",
          "created",
          "environment",
          "data"
        ]
      },
      "VideoTakeView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The take’s id."
          },
          "n": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "Take 1, 2, 3… in the order they were added."
          },
          "filename": {
            "type": "string",
            "description": "The name the file was sent with. Never part of a storage key."
          },
          "state": {
            "type": "string",
            "enum": [
              "uploading",
              "uploaded",
              "preparing",
              "ready",
              "failed",
              "discarded"
            ],
            "description": "`uploading` → `uploaded` → `preparing` → `ready`. Or `failed` (`failure` says why) or `discarded`."
          },
          "seconds": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "description": "How long the take runs, as SlyOS measured it. Null until measured."
          },
          "poster_url": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "A still from the take, good for an hour. Null until it is prepared."
          },
          "failure": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the take cannot be used, in a sentence. Set exactly when `state` is `failed`."
          },
          "note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Something worth knowing that is not a failure — \"No face found, so it is framed in the middle.\" Null when there is nothing."
          },
          "upload": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "part_bytes": {
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "maximum": 9007199254740991,
                    "description": "Bytes in every part but the last (16,777,216)."
                  },
                  "part_count": {
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "maximum": 9007199254740991,
                    "description": "How many parts the file is cut into."
                  },
                  "parts_done": {
                    "anyOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 10000
                        }
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Part numbers storage already holds — skip them when resuming. Null when SlyOS cannot list them: resume from your own record."
                  }
                },
                "required": [
                  "part_bytes",
                  "part_count",
                  "parts_done"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "While `state` is `uploading`: how the file is cut into parts and which parts storage holds. Null otherwise."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When the take was added."
          }
        },
        "required": [
          "id",
          "n",
          "filename",
          "state",
          "seconds",
          "poster_url",
          "failure",
          "note",
          "upload",
          "created_at"
        ]
      },
      "VideoUsageView": {
        "type": "object",
        "properties": {
          "included": {
            "type": "boolean",
            "description": "Whether the account’s plan makes videos at all."
          },
          "videos": {
            "type": "object",
            "properties": {
              "used": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991,
                "description": "Made so far. A change within a video’s allowance is not a new one."
              },
              "of": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991,
                "description": "What the plan includes."
              }
            },
            "required": [
              "used",
              "of"
            ],
            "description": "Videos this calendar month (UTC)."
          },
          "checks": {
            "type": "object",
            "properties": {
              "used": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991,
                "description": "Asked for so far."
              },
              "of": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991,
                "description": "What the plan includes."
              }
            },
            "required": [
              "used",
              "of"
            ],
            "description": "Checks this calendar month (UTC)."
          },
          "footage_pct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Footage uploaded this month, as a share of the plan’s, rounded down."
          },
          "resets_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When the counts start again: the first moment of next month, UTC."
          },
          "versions": {
            "anyOf": [
              {
                "type": "number",
                "const": 1
              },
              {
                "type": "number",
                "const": 3
              }
            ],
            "description": "1, or 3 cuts to pick from."
          },
          "keep_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Days a finished cut is kept before its files are deleted."
          },
          "take_max_bytes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "The largest one take may be."
          },
          "take_max_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "The longest one take may run."
          },
          "output_max_seconds": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "The longest a finished video may run."
          }
        },
        "required": [
          "included",
          "videos",
          "checks",
          "footage_pct",
          "resets_at",
          "versions",
          "keep_days",
          "take_max_bytes",
          "take_max_minutes",
          "output_max_seconds"
        ]
      },
      "WebhookTestEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "The event’s own id. A retry of the same event carries the same id: deduplicate on it."
          },
          "type": {
            "type": "string",
            "const": "webhook.test",
            "description": "The event type."
          },
          "created": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "When the event happened, in Unix seconds — the same on every retry, so an old event reads as old."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "Which of the account’s two endpoints it was sent to: `sandbox` or `live`."
          },
          "data": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "description": "A sentence saying it is a test."
              }
            },
            "required": [
              "message"
            ],
            "description": "Nothing but the sentence."
          }
        },
        "required": [
          "id",
          "type",
          "created",
          "environment",
          "data"
        ]
      },
      "WriteVideoScriptRequest": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "Your idempotency id, a UUID. The same one twice is one script, written once."
          },
          "topic": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "What the video is about, in your words."
          },
          "platform": {
            "default": "both",
            "type": "string",
            "enum": [
              "tiktok",
              "instagram",
              "both"
            ],
            "description": "`tiktok`, `instagram` or `both` (the default)."
          },
          "target_seconds": {
            "default": 45,
            "type": "integer",
            "minimum": 10,
            "maximum": 180,
            "description": "The length to aim for, 10–180 seconds. Default 45."
          },
          "notes": {
            "type": "string",
            "maxLength": 1000,
            "description": "Anything else the script should do: a tone, a point to make, a line to avoid."
          },
          "links": {
            "maxItems": 5,
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 2000
            },
            "description": "Up to 5 pages to draw on beside About you."
          }
        },
        "required": [
          "request_id",
          "topic"
        ],
        "additionalProperties": false
      }
    }
  }
}
