---
type: Reference
title: WhiteboardAnimation.ai Headless API
description: >
  API key authentication and typed HTTP contract for uploading a finished picture and retrieving drawing assets.
stale_after: 2026-12-15
updated_at: 2026-09-29
---

# WhiteboardAnimation.ai Headless API

The API turns a finished PNG, JPEG, or WebP picture into downloadable drawing plans and assets. It does not generate a picture from text or return an MP4. Studio video export runs in the browser.

## Get and use an API key

A workspace owner or admin creates keys at [Studio → API](https://whiteboardanimation.ai/studio/api), with a name and an expiry (30, 90 or 365 days, or none), and revokes them there. Creating a key needs a plan with API access (Pro). The raw key (it starts with `lede_`) is shown once; store it in a secret manager or the `WHITEBOARD_API_KEY` environment variable. A key acts for its workspace and spends that workspace's credits. Agents must not request browser cookies or ask users to paste a key into chat.

A key is checked against its workspace's plan on every request. When the workspace no longer has API access the key is not revoked: requests answer `403` with `error.code` `plan_required` until the workspace upgrades, and the same key then works again. An expired or revoked key answers `401` with `error.code` `invalid_api_key` whatever the plan: replace the key, upgrading will not revive it.

The key is the Bearer token; no OAuth flow or browser session is required. Use it on every `/api/v1/**` request. The signed upload and download URLs are separate object-storage URLs and must not receive the API key.

```sh
curl "https://whiteboardanimation.ai/api/v1/drawings/$DRAWING_ID" \
  --header "Authorization: Bearer $WHITEBOARD_API_KEY"
```

Full-flow permissions: `uploads:write`, `drawings:create`, `drawings:read`. A key without the required permission cannot call that endpoint. The current per-key configuration permits 60 successful verifications before a 60-second idle reset; it is **not** sustained throughput of 60 requests/minute. Poll deliberately, around every five seconds for one job, and obey `Retry-After` on `429`.

## Endpoint contracts

Base URL: `https://whiteboardanimation.ai`. JSON requests use `Content-Type: application/json`. Types below describe the wire format; comments state server-enforced limits. Omitted drawing settings use Studio defaults.

Endpoint: `POST /api/v1/uploads` · permission `uploads:write` · `201 Created`

```ts
interface CreateUploadInput {
	file_name: string // Trimmed; 1–120 characters.
	content_type: 'image/png' | 'image/jpeg' | 'image/webp'
	size: number // Actual file size in bytes; integer, 1–10 MiB.
}
```

Send pictures at most **1920 px on the long side** on a paid plan or after buying credits (**1024 px** on the free plan), ideally WebP: drawings are made at that size and larger pictures are scaled down anyway. Studio downscales in the browser before uploading.

```ts
interface CreateUploadResult {
	upload_id: string
	upload_url: string // Short-lived signed object-storage URL.
	upload_method: 'PUT'
	upload_headers: { 'Content-Type': CreateUploadInput['content_type'] }
	confirm_url: '/api/v1/uploads/confirm'
}
```

Send the file bytes to `upload_url` using `upload_method` and exactly `upload_headers`; do **not** attach the API key. Confirmation checks the stored object's actual metadata, not just the initial declaration.

Endpoint: `POST /api/v1/uploads/confirm` · permission `uploads:write` · `200 OK`

```ts
interface ConfirmUploadInput {
	upload_id: string
}

interface ConfirmUploadResult {
	upload_id: string
	status: 'ready'
}
```

Endpoint: `POST /api/v1/drawings` · permission `drawings:create` · `202 Accepted`

Header: `Idempotency-Key: <unique value>` — required, 1–120 characters after trimming. Reuse the **same** value on retries to avoid another computation.

```ts
interface CreateDrawingInput {
	upload_id: string // A confirmed upload owned by this key's user.
	title?: string // Trimmed; at most 120 characters.
	settings?: {
		line_duration?: number // Seconds, 1–600.
		color_duration?: number // Seconds, 1–600.
		hold?: 0 | 0.08 | 0.16 | 0.32
		color_order?: 'subject' | 'natural' | 'regions'
		strategy?: 'baseline' | 'regions' | 'attention' | 'cohesion'
		brush?: {
			seed: number // Integer.
			size: number // 0.1–8.
			swing: number // 0–8.
		}
	}
}

interface CreateDrawingResult {
	drawing_id: string // Job ID, not a video ID.
	work_id: string
	status: 'queued'
	status_url: string // Relative, same-origin path; also returned in Location.
}
```

Endpoint: `GET /api/v1/drawings/:id` · permission `drawings:read` · `200 OK`

Use the `drawing_id` or returned `status_url`; join relative paths only to this API's origin. Results are scoped to the key owner's account.

```ts
type DrawingStage =
	'input' | 'prepare' | 'line' | 'foreground' | 'encode' | 'plan' | 'store'

interface DrawingResult {
	drawing_id: string
	status: 'queued' | 'running' | 'succeeded' | 'failed'
	attempt: number
	progress: {
		stage: DrawingStage
		status: 'started' | 'completed' | 'failed'
		layerId?: string
		completed?: number
		total?: number
		elapsedMs?: number
	} | null
	error: {
		kind: 'input' | 'service' | 'offline' | 'busy'
		code: string
		message: string
		stage?: DrawingStage
		traceId?: string
	} | null
	downloads: Record<string, string> | null // Asset path → one-hour signed GET URL; no MP4.
}
```

`downloads` is populated only after success. Download the returned assets before their URLs expire, or read status again for fresh URLs.

## Errors

```ts
interface ApiError {
	error: {
		code: string
		message: string
		operation?: string
		retry_after_ms?: number
	}
}
```

`400` means malformed JSON or a missing/invalid idempotency key; `401` means missing, invalid, expired or under-permissioned key; `404` hides both absent and other-owner drawings; `409` means upload state conflict; `422` means invalid input; `429` includes `Retry-After` for key verification limits; `500`/`503` indicate service failure. Owner-level business limits are planned, not active. Retry a failed drawing submission with its original `Idempotency-Key`.
