# Authentication Source: https://docs.knowlify.com/api-reference/authentication How to authenticate calls to the Knowlify HTTP API ## Overview The Knowlify HTTP API supports two authentication methods. External integrations should use **API keys**; the JWT path is reserved for first-party dashboard sessions. | Method | Header | Use case | | ------------ | ----------------------------- | ----------------------------------------- | | API key | `X-API-Key: kn_<64 hex>` | Server-to-server, scripts, scheduled jobs | | Supabase JWT | `Authorization: Bearer ` | First-party dashboard / SDK sessions | API keys grant full access to your account or organization billing and should never be shipped in client-side code or committed to source control. Treat them like passwords. ## API keys API keys are 67 characters long: a `kn_` prefix followed by 64 hexadecimal characters. ``` X-API-Key: kn_4f3c8b1a9e7d2f5a6b8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3 ``` ### Issuing and rotating keys 1. Open the **Developer** tab in your dashboard. * Personal scope: `create.knowlify.com/p/dashboard?tab=developer` * Organization scope: `create.knowlify.com/org/{slug}/dashboard?tab=developer` (owner or admin only) 2. Click **Generate API key**. The key is shown once — copy it into your secret manager immediately. 3. Each account has a single active key. Generating a new key revokes the previous one immediately. 4. Click **Revoke key** to invalidate the current key without issuing a replacement. ### Personal vs. organization keys * **Personal keys** authenticate as your user. Jobs and credits are charged to your personal workspace. * **Organization keys** authenticate as the org. The org membership is pinned to the key — if the creator is removed from the org, the key stops working. Jobs and credits are charged to the org workspace. When a request body includes an `org_id` and the key is org-scoped, the two must match or the request is rejected with `400`. ### Allowlist For safety, API keys currently authorize only the following endpoints: * `POST /v1/videos` — create one or more video generation jobs * `GET /v1/videos/{uuid}` — poll the status of a job * `POST /v1/edits` — submit an edit on an existing video * `GET /v1/edits/{edit_id}` — poll an edit * `POST /v1/edits/{edit_id}/revert` — undo the most recent completed edit All other endpoints require an authenticated dashboard session. Reach out if you need additional endpoints exposed. ## Supabase JWT First-party clients (the Knowlify dashboard, the Python SDK in interactive mode) authenticate with a Supabase JWT: ``` Authorization: Bearer eyJhbGciOiJIUzI1NiIs... ``` The token is validated as `HS256` against the `authenticated` audience. JWT callers may optionally pass `org_id` in the request body — the server verifies active membership before honoring the scope. ## Failure modes | Status | When | | ------------------ | -------------------------------------------------------------- | | `401 Unauthorized` | Header missing, malformed, or refers to a revoked key | | `403 Forbidden` | JWT caller is not an active member of the requested `org_id` | | `400 Bad Request` | `org_id` in body does not match the org the API key belongs to | See [Errors](/api-reference/errors) for the full status code reference. # Submit edit Source: https://docs.knowlify.com/api-reference/create-edit Run an atomic edit (edit → frame regen → video sync) on an existing video ## Overview `POST /v1/edits` mutates an existing video with a natural-language instruction. A single call kicks off three internal phases — Claude rewrites the planner, frame images are regenerated for any changed scenes, and the video clips are restitched against the new planner. The endpoint returns immediately with an `edit_id` you poll with [`GET /v1/edits/{edit_id}`](/api-reference/poll-edit). This endpoint is asynchronous. The response confirms the edit was accepted (`queued` or `processing`); the full edit pipeline runs in the background and typically takes 30 seconds to a few minutes depending on how many scenes changed. ## Endpoint ``` POST {API_BASE}/v1/edits ``` Your `API_BASE` is shown in the Developer tab of your dashboard. The default production base is `https://api.knowlify.com`. ## Authentication Send your key in the `X-API-Key` header. The key must own the target `video_uuid` — either as the user who created the video, or as an active member of the organization that owns it. ```bash theme={null} X-API-Key: kn_<64 hex chars> ``` See [Authentication](/api-reference/authentication) for full details on key issuance, rotation, and JWT alternatives. ## Headers Your `kn_<64 hex>` API key. Opaque caller-chosen string. Replaying the same key within 24 hours returns the original response without creating a second edit. Use a fresh value per logical operation (e.g., a UUID); reuse it on retries of the same operation. ## Request body UUID of the video to edit. Must be a `corporate_engine_v2` row owned by the API key's user or organization. Natural-language instruction describing the change. 1–5000 characters. Examples: `"Make scene 2 more dramatic with closer camera angles"`, `"Replace the bird in scene 3 with a hawk"`, `"Add a new closing scene that summarizes the topic"`. Up to 5 reference image URLs to condition the edit. Each URL must: * Use the `https://` scheme. * Resolve to a public IP (private, loopback, link-local, and reserved ranges are rejected). * Return `Content-Type: image/*` and `Content-Length` ≤ 5 MB on a `HEAD` request. One redirect hop is followed; the redirect target is re-validated end-to-end. Optional aspect ratio override. One of `"16:9"` or `"9:16"`. Defaults to the target video's existing aspect ratio. Reserved for admin keys. Standard API keys must leave this `false`; the content-safety scan always runs for external callers. ## Response A successful call returns HTTP `202 Accepted` once the edit has been recorded and dispatched to the worker. Stable identifier for this edit. Pass to [`GET /v1/edits/{edit_id}`](/api-reference/poll-edit) to track progress and to [`POST /v1/edits/{edit_id} /revert`](/api-reference/revert-edit) to undo. Echo of the `video_uuid` you submitted. Initial status. `"processing"` when the worker can start immediately, `"queued"` when another edit on the same video is already in flight. 1-indexed position behind any other active or queued edits on the same video. `0` when this edit is the first to run. ### Error codes | Code | Cause | | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | The `edit_request` was rejected by the content-safety scan. | | `401` | `X-API-Key` header missing or invalid. | | `402` | Insufficient credits for the worst-case cost of the edit. See [Credit cost](#credit-cost). `detail` is `"INSUFFICIENT_CREDITS: edit could cost up to , have "`. | | `403` | The API key does not own `video_uuid`. | | `404` | `video_uuid` does not exist. | | `409` | The target video is still being built for the first time (e.g., `pipeline_status` is `lowlevel` or `generating_frames`) or has errored. Wait for the initial render to finish. | | `415` | A `reference_image_urls[]` entry returned the wrong Content-Type, missing Content-Length, or exceeded 5 MB. | | `422` | Pydantic validation failure (missing field, oversized `edit_request`, `reference_image_urls[]` longer than 5) or a reference URL failed the SSRF host check (non-https, private IP, second-hop redirect). | | `429` | Per-caller rate limit exceeded. See [Rate Limits](/api-reference/rate-limits). | All non-2xx responses share the standard envelope: `{ "detail": "" }`. See [Errors](/api-reference/errors) for the full reference. ## Code examples ```bash curl theme={null} curl -X POST "https://api.knowlify.com/v1/edits" \ -H "X-API-Key: kn_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440099" \ -d '{ "video_uuid": "550e8400-e29b-41d4-a716-446655440000", "edit_request": "Make scene 2 more dramatic with closer camera angles.", "reference_image_urls": [ "https://cdn.knowlify.com/assets/style-ref.jpg" ] }' ``` ```javascript JavaScript theme={null} const res = await fetch("https://api.knowlify.com/v1/edits", { method: "POST", headers: { "X-API-Key": "kn_YOUR_KEY_HERE", "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ video_uuid: "550e8400-e29b-41d4-a716-446655440000", edit_request: "Make scene 2 more dramatic with closer camera angles.", reference_image_urls: ["https://cdn.knowlify.com/assets/style-ref.jpg"], }), }); const { edit_id, status } = await res.json(); console.log(`${edit_id} → ${status}`); ``` ```python Python theme={null} import requests import uuid resp = requests.post( "https://api.knowlify.com/v1/edits", headers={ "X-API-Key": "kn_YOUR_KEY_HERE", "Idempotency-Key": str(uuid.uuid4()), }, json={ "video_uuid": "550e8400-e29b-41d4-a716-446655440000", "edit_request": "Make scene 2 more dramatic with closer camera angles.", "reference_image_urls": [ "https://cdn.knowlify.com/assets/style-ref.jpg", ], }, timeout=30, ) resp.raise_for_status() body = resp.json() print(body["edit_id"], body["status"]) ``` #### Example success response ```json theme={null} { "edit_id": "e942890a-7776-4029-82d6-ce733d0a2cb2", "video_uuid": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "queue_position": 0 } ``` ## How edits work Internally a single `POST /v1/edits` runs three phases in sequence inside one worker job: 1. **Edit** — Claude reads the current planner and rewrites the affected scenes based on `edit_request`. Sets `pipeline_status` to `edit_complete`. 2. **Frame regen** — first/last frame images are regenerated for any changed or added scenes. Sets `pipeline_status` to `edit_pending_apply`. 3. **Sync** — video clips for changed and added scenes are regenerated and stitched into the existing timeline, producing a new `link` and `timestamps`. Sets `pipeline_status` back to `complete`. If the target video has never been fully rendered (no `timestamps` / `link` exists), phase 3 is **skipped automatically**. The edit lives in the planner and the next render will reflect it. `result.video_synced` on the poll response will be `false` in that case. If Claude returns a clarifying question instead of an edit (a plan-mode response), the endpoint auto-confirms with `"yes, proceed"` up to twice. If a third call still returns a question, the edit terminates with `status: "error"` and `error_message: "edit request was ambiguous; rephrase and retry."`. ## Credit cost Phase 1 (the Claude edit itself) is free. Phases 2 and 3 are billed **per scene** that successfully regenerates, after the underlying provider call returns: | Phase | Cost per scene | Charged for | | --------------------- | -------------- | -------------------------------------------------------------------------------------------- | | Frame regeneration | **4 credits** | Each Veo scene whose first/last frame is regenerated in phase 2. | | Video clip (Veo/grok) | **25 credits** | Each Veo scene whose video clip is regenerated in phase 3 (same rate as the initial render). | | Video clip (Remotion) | **9 credits** | Each Remotion scene that is re-rendered in phase 3. | You only pay for scenes that actually regenerate. A scene that fails partway through is never billed, and worker-level retries of the same edit are deduplicated so you cannot be charged twice for the same scene. ### Pre-flight check Before accepting the edit, `POST /v1/edits` computes a worst-case upper bound assuming every scene in the planner is touched: ``` upper_bound = (4 + 25) × veo_scenes + 9 × remotion_scenes ``` If your account balance is below this number the request is rejected with `402 Insufficient Credits` and no work is started. The actual charge after the edit completes is almost always lower — typically only one or a few scenes change. If the target video has never been fully rendered, phase 3 is skipped (see the note in [How edits work](#how-edits-work)) and you are only charged for phase 2. ## Next step The response gives you an `edit_id`. Use it with [`GET /v1/edits/{edit_id}`](/api-reference/poll-edit) to track the edit through `queued` → `processing` → `applying` → `complete`, and [`POST /v1/edits/{edit_id}/revert`](/api-reference/revert-edit) to undo a completed edit. ## Limits & errors * 10 requests / 60 seconds per caller. See [Rate Limits](/api-reference/rate-limits). * One video per request; concurrent edits on the same video are queued serially via `pending_edits[]`. * Full status code reference: [Errors](/api-reference/errors). # Create video Source: https://docs.knowlify.com/api-reference/create-video Queue 1–50 video generation jobs in a single request ## Overview `POST /v1/videos` is the programmatic entry point for the Knowlify Animation Engine. A single call queues 1–50 video generation jobs and returns immediately with a job ID for each one. Videos render asynchronously in the background — use [`GET /v1/videos/{uuid}`](/api-reference/poll-video) to poll, or subscribe to Supabase realtime for live updates. This endpoint is asynchronous. The response confirms each job was accepted (`queued` or `parked`); rendering itself takes minutes and is delivered out-of-band. ## Endpoint ``` POST {API_BASE}/v1/videos ``` Your `API_BASE` is shown in the Developer tab of your dashboard. The default production base is `https://api.knowlify.com`. ## Authentication Send your key in the `X-API-Key` header. Personal keys are scoped to your user; organization keys are scoped to a single org. ```bash theme={null} X-API-Key: kn_<64 hex chars> ``` See [Authentication](/api-reference/authentication) for full details on key issuance, rotation, and JWT alternatives. ## Request body Array of 1–50 video specs to queue. Items beyond your concurrency limit are accepted but parked in a waiting list — see [Rate Limits](/api-reference/rate-limits#concurrency). Advisory only — when authenticating with an API key, identity comes from the key itself. Maximum 320 characters; must match `local@domain.tld`. Optional organization scope. For API keys this must match the org the key belongs to (or be omitted). For JWT callers, the server verifies active membership. ### `VideoCreateItem` The instruction describing what the video should explain or show. 1–5000 characters. Target video length in seconds. Range: `30`–`300`. Output aspect ratio. One of `"16:9"` (landscape) or `"9:16"` (portrait). Style preset. One of: `"instructional"`, `"explainer"`, `"corporate"`, `"marketing"`, `"narrative"`, `"social"`. Free-form direction applied to every scene (e.g., "minimal, brand-blue accents, sans-serif"). Maximum 5000 characters. Optional brand palette. Allowed keys: `primary`, `secondary`, `tertiary`, `accent`. Each value must be a hex color (`#fff` or `#ffffff`). ```json theme={null} { "primary": "#0066cc", "accent": "#ff6600" } ``` Voice provider ID for narration. Maximum 200 characters. Display name for the chosen voice. Maximum 200 characters. Free-form description of the desired narration style. Maximum 2000 characters. Up to 20 reference images. Each entry can be an `http(s)` URL string or an object `{ "url": "..." }`. A single user-uploaded reference video (`http(s)` URL, ≤60s). `http(s)` URL of a source PDF to draw content from. Display name for the PDF (used for chat context). Maximum 500 characters. Up to 50 image selections extracted from the PDF. Internal mode flag. Leave as default unless instructed otherwise. Skips the manual voiceover-approval step. Always `true` for programmatic calls. ## Response A successful call returns HTTP `200` even when individual items fail — inspect each `results[].status` to know what to retry. Always `"ok"` on a 2xx response. Number of items in the request. Items dispatched to a worker immediately. Items accepted but waiting for a free concurrency slot. Items that failed validation or queueing. UUID of the authenticated caller. Organization ID if the key/JWT is org-scoped, otherwise `null`. One entry per request item, in the same order as `videos`. Position of this item in the original `videos` array. The job ID — pass to `GET /v1/videos/{uuid}` to poll status. Chat session ID for the job. Direct link to the dashboard view for this job. One of `"queued"`, `"parked"`, `"already_processing"`, or `"error"`. Present when `status` is `"parked"` — 1-indexed position in your waiting list. Present when `status` is `"error"` (e.g., `"ITEM_FAILED"`). Present when `status` is `"error"` — human-readable description. ### Response headers Every successful response carries the current rate-limit window state: ``` X-RateLimit-Limit: 30 X-RateLimit-Remaining: 27 X-RateLimit-Reset: 42 ``` ## Code examples ```bash curl theme={null} curl -X POST "https://api.knowlify.com/v1/videos" \ -H "X-API-Key: kn_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "task": "Explain how HTTPS works in 30 seconds", "video_duration_seconds": 60, "aspect_ratio": "16:9", "video_type": "explainer", "color_palette": { "primary": "#0066cc", "accent": "#ff6600" } }, { "task": "Quarterly results recap", "video_duration_seconds": 45, "aspect_ratio": "9:16", "video_type": "corporate" } ] }' ``` ```javascript JavaScript theme={null} const res = await fetch("https://api.knowlify.com/v1/videos", { method: "POST", headers: { "X-API-Key": "kn_YOUR_KEY_HERE", "Content-Type": "application/json", }, body: JSON.stringify({ videos: [ { task: "Explain how HTTPS works in 30 seconds", video_duration_seconds: 60, aspect_ratio: "16:9", video_type: "explainer", color_palette: { primary: "#0066cc", accent: "#ff6600" }, }, { task: "Quarterly results recap", video_duration_seconds: 45, aspect_ratio: "9:16", video_type: "corporate", }, ], }), }); const data = await res.json(); console.log(data.results.map((r) => `${r.uuid} → ${r.status}`)); ``` ```python Python theme={null} import requests resp = requests.post( "https://api.knowlify.com/v1/videos", headers={"X-API-Key": "kn_YOUR_KEY_HERE"}, json={ "videos": [ { "task": "Explain how HTTPS works in 30 seconds", "video_duration_seconds": 60, "aspect_ratio": "16:9", "video_type": "explainer", "color_palette": {"primary": "#0066cc", "accent": "#ff6600"}, }, { "task": "Quarterly results recap", "video_duration_seconds": 45, "aspect_ratio": "9:16", "video_type": "corporate", }, ] }, timeout=30, ) resp.raise_for_status() for r in resp.json()["results"]: print(r["uuid"], r["status"]) ``` ### Example success response ```json theme={null} { "status": "ok", "total": 2, "enqueued": 2, "parked": 0, "errors": 0, "user_id": "5b9e...c1", "org_id": null, "results": [ { "index": 0, "uuid": "550e8400-e29b-41d4-a716-446655440000", "session_id": "550e8400-e29b-41d4-a716-446655440001", "chat_url": "https://create.knowlify.com/p/chat/550e8400-e29b-41d4-a716-446655440001", "status": "queued" }, { "index": 1, "uuid": "550e8400-e29b-41d4-a716-446655440002", "session_id": "550e8400-e29b-41d4-a716-446655440003", "chat_url": "https://create.knowlify.com/p/chat/550e8400-e29b-41d4-a716-446655440003", "status": "parked", "queue_position": 1 } ] } ``` ## Next step Each accepted item returns a `uuid`. Use it with [`GET /v1/videos/{uuid}`](/api-reference/poll-video) to track render progress until the job is complete. ## Limits & errors * 30 requests / 60 seconds per caller. See [Rate Limits](/api-reference/rate-limits). * 1–50 items per request; max 3 concurrent jobs per user/org (additional items are parked). * Full status code reference: [Errors](/api-reference/errors). # Errors Source: https://docs.knowlify.com/api-reference/errors HTTP status codes and per-item error shapes for the Knowlify API ## Status codes | Code | Meaning | Typical cause | | ----- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `200` | Success | Request accepted. Inspect `results[].status` for per-item outcomes — some may still be `"error"`. | | `400` | Bad Request | Pydantic validation failed (bad enum, oversized batch, malformed URL or hex color), or `org_id` in body mismatches the API key's org. | | `401` | Unauthorized | `X-API-Key` / `Authorization` header missing, malformed, or revoked. | | `402` | Payment Required | `/v1/edits` rejected the request because the account balance is below the worst-case credit cost of the edit. `detail` is `"INSUFFICIENT_CREDITS: edit could cost up to , have "`. See [Credit cost](/api-reference/create-edit#credit-cost). | | `403` | Forbidden | JWT caller is not an active member of the requested organization, or an API key is being used against a resource it does not own. | | `404` | Not Found | The target `video_uuid` or `edit_id` does not exist. | | `409` | Conflict | `/v1/edits` — target video is still being built or has errored; or `/v1/edits/{edit_id}/revert` was rejected by one of the revert guards (active edit on the video, a newer external edit exists, or the top of `planner_history` does not match this edit). | | `415` | Unsupported Media Type | A `reference_image_urls[]` entry returned the wrong `Content-Type` (must be `image/*`), exceeded 5 MB, or was missing `Content-Length` on the upstream `HEAD` response. | | `422` | Unprocessable Entity | Body is not valid JSON, or a reference image URL failed the SSRF host check (non-https, private/loopback/link-local IP, or more than one redirect hop). | | `429` | Too Many Requests | Per-caller rate limit exceeded. See [Rate Limits](/api-reference/rate-limits). | | `500` | Internal Server Error | Unexpected server failure. Per-item failures normally surface as `200` with `status: "error"` rows; a `500` indicates the request itself could not be handled. | All non-2xx responses share the same envelope: ```json theme={null} { "detail": "" } ``` ## Per-item errors `POST /v1/videos` returns `200` even when some items fail validation or queueing — your client should iterate `results` and retry only the failed entries. A failed result entry looks like: ```json theme={null} { "index": 3, "uuid": "550e8400-e29b-41d4-a716-446655440099", "session_id": "...", "chat_url": "...", "status": "error", "code": "ITEM_FAILED", "message": "voice_id was rejected by the upstream provider" } ``` | Field | Meaning | | --------- | --------------------------------------------------------------------------------------- | | `status` | Always `"error"` for failed items. | | `code` | Machine-readable error code. Currently `"ITEM_FAILED"` is the only value emitted. | | `message` | Human-readable description suitable for logs. | | `index` | Position of the failed item in the original request — use to correlate with your input. | ## Retry guidance | Status | Retry? | How | | ----------------------------- | ----------------- | ------------------------------------------------- | | `429` | Yes | Wait `Retry-After` seconds, then retry. | | `500` | Yes, with backoff | Exponential backoff starting at 2 s. | | `400` / `401` / `403` / `422` | No | Fix the request first. | | `402` | After top-up | Add credits to the account, then resubmit. | | `200` with per-item `"error"` | Yes, per item | Resubmit only the failing items in a new request. | If you see repeated `500`s on a previously working integration, email [info@knowlify.com](mailto:info@knowlify.com) with a request ID. # API Reference Source: https://docs.knowlify.com/api-reference/introduction HTTP API for the Knowlify Video Generation Engine ## Overview Knowlify exposes two HTTP endpoints for video generation — one to queue jobs, one to poll their progress: `POST /v1/videos` — queue 1–50 jobs in one call. Async; returns job IDs to poll. `GET /v1/videos/{uuid}` — check render progress until `is_complete` is `true`. The flow is async by design — `POST /v1/videos` accepts a batch and returns job IDs immediately, while rendering happens in the background. Poll `GET /v1/videos/{uuid}` for status, or subscribe to Supabase realtime updates on the returned `uuid` for live progress. ## Shared guides `X-API-Key` issuance, rotation, and scope rules. 30 req/min, 1–50 items per call, 3 concurrent jobs. HTTP status codes and per-item error shapes. ## Getting started 1. Open the **Developer** tab in your dashboard and generate an API key. 2. Read the [Authentication](/api-reference/authentication) guide for the header format. 3. Make your first call using the curl or JavaScript snippet on the [Create video](/api-reference/create-video) page. 4. [Poll video status](/api-reference/poll-video) (or subscribe via Supabase realtime) until `is_complete` is `true`. # Poll edit status Source: https://docs.knowlify.com/api-reference/poll-edit Track an in-flight edit and read the final result ## Overview After [submitting an edit](/api-reference/create-edit), use the returned `edit_id` to poll for progress. The endpoint accepts the same `X-API-Key` you used to create the edit, and only the original submitter (or an active member of the submitter's organization) may read the edit. ``` GET {API_BASE}/v1/edits/{edit_id} ``` Your `API_BASE` is shown in the Developer tab of your dashboard. The default production base is `https://api.knowlify.com`. ## Authentication Send your key in the `X-API-Key` header — the same key (personal or organization) that submitted the edit. ```bash theme={null} X-API-Key: kn_<64 hex chars> ``` See [Authentication](/api-reference/authentication) for details on key issuance, rotation, and JWT alternatives. ## Path parameters The edit ID returned by `POST /v1/edits`. ## Code examples ```bash curl theme={null} curl "https://api.knowlify.com/v1/edits/e942890a-7776-4029-82d6-ce733d0a2cb2" \ -H "X-API-Key: kn_YOUR_KEY_HERE" ``` ```javascript JavaScript theme={null} const res = await fetch( `https://api.knowlify.com/v1/edits/${editId}`, { headers: { "X-API-Key": "kn_YOUR_KEY_HERE" } }, ); const edit = await res.json(); if (edit.is_complete) { console.log("New video link:", edit.result.link); } ``` ```python Python theme={null} import requests resp = requests.get( f"https://api.knowlify.com/v1/edits/{edit_id}", headers={"X-API-Key": "kn_YOUR_KEY_HERE"}, timeout=15, ) resp.raise_for_status() edit = resp.json() if edit["is_complete"]: print("New video link:", edit["result"]["link"]) ``` ## Response The edit ID (echoes the path parameter). The video this edit was submitted against. Current state. One of: * `queued` — another edit on the same video is in flight; this one will run after it finishes. * `processing` — phase 1 (Claude edit) is running. * `applying` — phase 3 (video sync) is running. * `complete` — all three phases finished successfully. * `error` — a phase failed. See `error_message`. * `reverted` — this edit was undone by [`POST /v1/edits/{edit_id}/revert`](/api-reference/revert-edit). `true` when `status` is `complete` or `reverted`. `true` when `status` is `error`. Inspect `error_message`. One of `queued`, `editing`, `regenerating_frames`, `applying_edits`, `done`. Coarse 0–100 progress estimate. Updates at phase boundaries (5 → 40 → 75 → 100); it does not advance continuously inside a phase. Present during `regenerating_frames` — the scene numbers being regenerated. Present once `status` is `complete` or `reverted`. The new video URL, or `null` when phase 3 was skipped because the target video had never been rendered. See `video_synced`. Scenes whose content was modified by the edit. New scenes inserted by the edit. Scenes removed by the edit. Scenes whose video clips were regenerated during phase 3. Scenes whose clips were carried forward unchanged. `false` when the target video had no rendered timestamps and phase 3 was skipped — the planner reflects the edit but no new video was produced. Present only on `status: "reverted"` responses. Contains `changed_scene_numbers`, `added_scene_numbers`, `deleted_scene_numbers` describing what the revert restored. Human-readable failure reason. `null` on healthy edits. When a phase fails because the account ran out of credits mid-flight, this field is prefixed with `INSUFFICIENT_CREDITS:`. Top up the account and resubmit the edit. See [Credit cost](/api-reference/create-edit#credit-cost) for the per-scene rates and the pre-flight check that normally catches this at submission time. ISO-8601 timestamp when the edit was accepted. ISO-8601 timestamp of the last status change. Use this to detect stalled edits. #### Example response (mid-flight) ```json theme={null} { "edit_id": "e942890a-7776-4029-82d6-ce733d0a2cb2", "video_uuid": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "is_complete": false, "is_failed": false, "progress": { "stage": "regenerating_frames", "percent": 40, "scene_numbers": [2] }, "result": null, "error_message": null, "created_at": "2026-05-25T15:30:01.000000+00:00", "updated_at": "2026-05-25T15:30:42.123000+00:00" } ``` #### Example response (complete) ```json theme={null} { "edit_id": "e942890a-7776-4029-82d6-ce733d0a2cb2", "video_uuid": "550e8400-e29b-41d4-a716-446655440000", "status": "complete", "is_complete": true, "is_failed": false, "progress": { "stage": "done", "percent": 100 }, "result": { "link": "https://animation-encoder-videos.s3.us-west-2.amazonaws.com/.mp4", "changed_scene_numbers": [2], "added_scene_numbers": [], "deleted_scene_numbers": [], "regenerated_scenes": [2], "kept_scenes": [1, 3, 4, 5], "video_synced": true }, "error_message": null, "created_at": "2026-05-25T15:30:01.000000+00:00", "updated_at": "2026-05-25T15:32:18.500000+00:00" } ``` Poll at most once every 5 seconds — `progress.percent` advances at phase boundaries, not per-scene. Production integrations should subscribe to Supabase realtime updates on the `external_edits` table instead. Reach out for a Supabase anon key scoped to your account. ## Limits & errors * The polling endpoint shares the 10 requests / 60 seconds rate limit with `POST /v1/edits`. See [Rate Limits](/api-reference/rate-limits). * Full status code reference: [Errors](/api-reference/errors). # Poll video status Source: https://docs.knowlify.com/api-reference/poll-video Track render progress and completion of a queued generation job ## Overview After [creating a video](/api-reference/create-video), use the returned `uuid` to poll for progress. The endpoint accepts the same `X-API-Key` you used to create the job. ``` GET {API_BASE}/v1/videos/{uuid} ``` Your `API_BASE` is shown in the Developer tab of your dashboard. The default production base is `https://api.knowlify.com`. ## Authentication Send your key in the `X-API-Key` header — the same key (personal or organization) that created the job. ```bash theme={null} X-API-Key: kn_<64 hex chars> ``` See [Authentication](/api-reference/authentication) for details on key issuance, rotation, and JWT alternatives. ## Path parameters The job ID returned in `results[].uuid` from `POST /v1/videos`. ## Code examples ```bash curl theme={null} curl "https://api.knowlify.com/v1/videos/550e8400-e29b-41d4-a716-446655440000" \ -H "X-API-Key: kn_YOUR_KEY_HERE" ``` ```javascript JavaScript theme={null} const res = await fetch(`https://api.knowlify.com/v1/videos/${uuid}`, { headers: { "X-API-Key": "kn_YOUR_KEY_HERE" }, }); const status = await res.json(); if (status.is_complete) { console.log("Video ready"); } ``` ```python Python theme={null} import requests resp = requests.get( f"https://api.knowlify.com/v1/videos/{uuid}", headers={"X-API-Key": "kn_YOUR_KEY_HERE"}, timeout=15, ) resp.raise_for_status() status = resp.json() if status["is_complete"]: print("Video ready") ``` ## Response The job ID (echoes the path parameter). Current pipeline stage. One of: `pending`, `queued`, `voiceover`, `highlevel`, `lowlevel`, `scan_assets`, `generating_frames`, `complete`, `failed`. `true` once rendering finished successfully. `true` if the job stopped due to an unrecoverable error. Inspect `error_message`. Same as top-level `status`. Coarse 0–100 progress estimate. Stage-driven, with a finer-grained band during `generating_frames` based on `current_scene` / `total_scenes`. Scenes rendered so far (only meaningful during `generating_frames`). Total scenes for this video. Human-readable failure reason. `null` on healthy jobs. The original `task` prompt for this job (echoed for convenience). ISO-8601 timestamp when the job was first queued. ISO-8601 timestamp of the last status change. Use this to detect stalled jobs. #### Example response (mid-render) ```json theme={null} { "uuid": "550e8400-e29b-41d4-a716-446655440000", "status": "generating_frames", "is_complete": false, "is_failed": false, "progress": { "stage": "generating_frames", "percent": 88, "current_scene": 4, "total_scenes": 5 }, "error_message": null, "task": "Explain how HTTPS works in 30 seconds", "created_at": "2026-04-25T12:34:56.789012+00:00", "updated_at": "2026-04-25T12:36:18.123456+00:00" } ``` #### Example response (complete) ```json theme={null} { "uuid": "550e8400-e29b-41d4-a716-446655440000", "status": "complete", "is_complete": true, "is_failed": false, "progress": { "stage": "complete", "percent": 100, "current_scene": 5, "total_scenes": 5 }, "error_message": null, "task": "Explain how HTTPS works in 30 seconds", "created_at": "2026-04-25T12:34:56.789012+00:00", "updated_at": "2026-04-25T12:39:42.500000+00:00" } ``` Poll at most once every 5 seconds — status changes are stage-level, not per-frame. Production integrations should subscribe to Supabase realtime updates on the `corporate_engine_v2` table. Reach out for a Supabase anon key scoped to your account. ## Limits & errors * The same 30 requests / 60 seconds rate limit applies to polling. See [Rate Limits](/api-reference/rate-limits). * Full status code reference: [Errors](/api-reference/errors). # Rate Limits Source: https://docs.knowlify.com/api-reference/rate-limits Request, batch, and concurrency limits for the Knowlify HTTP API ## Overview Knowlify enforces four independent limits on the HTTP API. Each is keyed per caller — personal users have their own pool; each organization has its own pool. | Limit | Value | Behavior on excess | | ------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------ | | Videos request rate | 30 requests / 60 s on `POST /v1/videos` and `GET /v1/videos/{uuid}` | `429 Too Many Requests` | | Edits request rate | 10 requests / 60 s on `POST /v1/edits`, `GET /v1/edits/{edit_id}`, `POST /v1/edits/{edit_id}/revert` | `429 Too Many Requests` | | Batch size | 1–50 items per `POST /v1/videos` | `400 Bad Request` | | Concurrent jobs | 3 active jobs per caller | Excess items are *parked* (queued for promotion) | ## Request rate A fixed 60-second window per caller, counted in Redis. The identity used for keying is: * `user_id` for personal calls (JWT or personal API key) * `org:` for organization calls (org API key, or JWT with `org_id` in body) Every successful response includes the current window state: ``` X-RateLimit-Limit: 30 X-RateLimit-Remaining: 27 X-RateLimit-Reset: 42 ``` * `X-RateLimit-Limit` — the cap (`30`) * `X-RateLimit-Remaining` — requests left in the current window * `X-RateLimit-Reset` — seconds until the window resets When you exceed the cap, the response is `429` with a `Retry-After` header: ```http theme={null} HTTP/1.1 429 Too Many Requests Retry-After: 45 X-RateLimit-Limit: 30 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 45 Content-Type: application/json { "detail": "Rate limit exceeded: 30 requests per 60s. Retry in 45s." } ``` Use `X-RateLimit-Remaining` to self-pace. If a single 50-item batch is fine for your workload, prefer one batched call over 50 single-item calls. ### Separate bucket for edits The `/v1/edits` endpoints have their own counter (10 requests / 60 s by default), independent from the `/v1/videos` 30 / 60 s bucket. Edits are typically cheaper than full renders but submitted more frequently, so the two limits don't share state — bursting on `/v1/edits` won't eat your `/v1/videos` quota. The same per-caller identity (`user_id` or `org:`) keys both buckets. ## Batch size A single `POST /v1/videos` request must contain between 1 and 50 entries in the `videos` array. The server rejects empty arrays and arrays of more than 50 items with `400` and a Pydantic validation error in `detail`. ## Concurrent jobs Each caller may have up to **3** jobs actively rendering at any moment. When you submit more than 3 items in a single batch (or submit while you already have jobs in flight), excess items are accepted but marked `"parked"`: ```json theme={null} { "index": 4, "uuid": "...", "status": "parked", "queue_position": 2 } ``` The Knowlify worker promotes parked jobs in FIFO order as active slots free up — there's nothing for you to retry. Poll [`GET /v1/videos/{uuid}`](/api-reference/poll-video) to watch the job advance from `pending` → `queued` → rendering stages. Personal and organization pools are independent. A user who is also an org admin can have 3 personal jobs *and* 3 org jobs running concurrently. ## Need higher limits? Email [info@knowlify.com](mailto:info@knowlify.com) with your account ID and expected volume. # Revert edit Source: https://docs.knowlify.com/api-reference/revert-edit Undo the most recent completed edit on a video ## Overview `POST /v1/edits/{edit_id}/revert` restores the planner snapshot saved before this edit was applied, rewinds the video clips and timestamps, and updates the edit's `status` to `reverted`. Strict LIFO — only the most recent completed external edit on a video can be reverted; older edits must wait for any newer ones to be reverted first. Revert runs three preconditions. All three must pass before the snapshot is restored: 1. **No active edits.** Nothing on the video is `processing` or `queued`. 2. **Strict LIFO.** No newer external edit (non-error, non-reverted) exists for this video. 3. **Snapshot correlation.** The top of the video's `planner_history` was saved by this edit — guards against a frontend edit landing in between. If any guard fails, the response is `409` with a `detail` that names the violated rule. ## Endpoint ``` POST {API_BASE}/v1/edits/{edit_id}/revert ``` Your `API_BASE` is shown in the Developer tab of your dashboard. The default production base is `https://api.knowlify.com`. ## Authentication Send your key in the `X-API-Key` header. Only the API key that submitted the edit (or another active key in the same organization) may revert it. ```bash theme={null} X-API-Key: kn_<64 hex chars> ``` See [Authentication](/api-reference/authentication) for details on key issuance, rotation, and JWT alternatives. ## Path parameters The edit ID returned by `POST /v1/edits`. The edit must be in `status: "complete"`. ## Request body No body. The path parameter fully specifies the operation. ## Code examples ```bash curl theme={null} curl -X POST "https://api.knowlify.com/v1/edits/e942890a-7776-4029-82d6-ce733d0a2cb2/revert" \ -H "X-API-Key: kn_YOUR_KEY_HERE" ``` ```javascript JavaScript theme={null} const res = await fetch( `https://api.knowlify.com/v1/edits/${editId}/revert`, { method: "POST", headers: { "X-API-Key": "kn_YOUR_KEY_HERE" }, }, ); const edit = await res.json(); console.log(edit.status, edit.result.revert_diff); ``` ```python Python theme={null} import requests resp = requests.post( f"https://api.knowlify.com/v1/edits/{edit_id}/revert", headers={"X-API-Key": "kn_YOUR_KEY_HERE"}, timeout=30, ) resp.raise_for_status() edit = resp.json() print(edit["status"], edit["result"]["revert_diff"]) ``` ## Response A successful revert returns `200` with the same shape as [`GET /v1/edits/{edit_id}`](/api-reference/poll-edit), now reflecting the reverted state: * `status` is `"reverted"`. * `is_complete` is `true`. * `result` keeps the original `link` / scene-number arrays from the completed edit, plus a new `revert_diff` describing what the restore changed. Scenes whose content the revert restored from the snapshot. Scenes that the original edit had deleted and the revert brought back. Scenes that the original edit had added and the revert removed. All other response fields are identical to the poll endpoint. ### Failure modes | Code | `detail` | Cause | | ----- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `404` | `edit not found` | No `external_edits` row matches `edit_id`. | | `403` | `API key does not own this edit` | The caller is neither the submitter nor an active member of the submitter's org. | | `409` | `edit is not in a revertable state (status=…)` | The edit is still `processing` / `applying`, already `reverted`, or `error`. | | `409` | `cannot revert while another edit is processing or queued` | Another edit on the same video is in flight. Wait for it to finish. | | `409` | `a newer external edit exists on this video; revert the most recent edit first` | LIFO violation. Revert the newer edit first. | | `409` | `snapshot at the top of planner_history was not saved by this edit; cannot safely revert` | A frontend-initiated edit landed after this one, pushing a different snapshot on top. The revert cannot proceed without undoing somebody else's work. | | `409` | `no snapshot available to revert` | The video's `planner_history` is empty (rare; suggests the edit ran but the snapshot was never saved). | All non-2xx responses share the standard envelope: `{ "detail": "" }`. See [Errors](/api-reference/errors) for the full reference. #### Example success response ```json theme={null} { "edit_id": "e942890a-7776-4029-82d6-ce733d0a2cb2", "video_uuid": "550e8400-e29b-41d4-a716-446655440000", "status": "reverted", "is_complete": true, "is_failed": false, "progress": { "stage": "done", "percent": 100 }, "result": { "link": "https://animation-encoder-videos.s3.us-west-2.amazonaws.com/.mp4", "changed_scene_numbers": [2], "added_scene_numbers": [], "deleted_scene_numbers": [], "regenerated_scenes": [2], "kept_scenes": [1, 3, 4, 5], "video_synced": true, "revert_diff": { "changed_scene_numbers": [2], "added_scene_numbers": [], "deleted_scene_numbers": [] } }, "error_message": null, "created_at": "2026-05-25T15:30:01.000000+00:00", "updated_at": "2026-05-25T15:45:11.000000+00:00" } ``` ## Limits & errors * Revert is synchronous from the caller's perspective — the response returns once the planner has been restored. There is no separate polling step. * Frame regeneration after a revert is triggered automatically when the restored planner differs from the current one (the internal `/revert-edit` pipeline handles this). * Full status code reference: [Errors](/api-reference/errors). # Welcome to Knowlify Source: https://docs.knowlify.com/index Generate animated-style videos with AI-powered automation Knowlify turns prompts into narrated, animated videos. The HTTP API is built for batch and async workflows — a single call queues 1–50 jobs and returns job IDs you can poll or stream over Supabase realtime. ## Get started `POST /v1/videos` — queue 1–50 video generation jobs in a single call. `GET /v1/videos/{uuid}` — track render progress until the job completes. Issue an `X-API-Key` from the Developer tab and start calling the API. ## Reference Request rate, batch size, and concurrent-job caps. HTTP status codes and per-item error shapes.