# Mosaic Motion Full Documentation This document is optimized for LLMs and mirrors the public Mosaic Motion documentation. ## Overview Mosaic Motion creates durable Rio Motion-agent video generation jobs from prompts. A request can include: - A required prompt. - Optional aspect ratio and duration bucket config. - Optional Pro/Max preset design system or custom DESIGN.md content. - An optional YouTube style reference URL. - Up to 10 attachments, each represented by a publicly accessible signed URL. The API returns a `job_id` immediately. Clients poll the job status endpoint. `status_url` is the authenticated public API polling URL; `chat_url` is the Motion app chat URL for a human to open, inspect, or continue the run. Base URL: ```txt https://api.motion.so/api/motion ``` Authentication: ```http Authorization: Bearer motion_your_api_key ``` Mosaic Motion API keys start with `motion_`. Jobs are owned by the user who created the API key. API initiated runs use the Rio Motion agent with durable Workflow orchestration and auto approve planning stages where possible. API jobs require available Mosaic Motion credits on the API key owner's account. Purchase credits and configure auto top-ups in the Motion frontend at https://motion.so. Credit purchasing and auto top-up settings are not available through the API. ## Create Job Endpoint: ```http POST /sessions ``` Full URL: ```txt https://api.motion.so/api/motion/sessions ``` Headers: ```http Authorization: Bearer motion_your_api_key Content-Type: application/json ``` Request body schema: ```json { "prompt": "string, required, 1 to 12000 chars", "aspect_ratio": "string, optional: 16:9 | 9:16 | 1:1 | 4:5 | 21:9", "duration": "string, optional: <10s | 10-30s | 30s-1min | 1-5min", "design_system_id": "string, optional Pro/Max preset: mosaic | apple | claude | cursor | linear | vercel | stripe | figma | notion | spotify | supabase | raycast | framer | resend | mintlify | sentry | tesla | nike | shopify | airbnb | posthog", "design_md": "string or object, optional Pro/Max custom DESIGN.md content. Object form: { filename?: string, content: string, jsonFilename?: string, jsonContent?: string }", "style_reference_url": "string, optional, YouTube URL", "attachments": [ { "url": "string, required, publicly accessible signed URL, max 4096 chars", "name": "string, optional, max 240 chars", "type": "string, optional: image | video | audio | file", "content_type": "string, optional MIME type hint" } ] } ``` Attachment limit: maximum 10 attachments. Attachment URLs must be publicly reachable http(s) links. Local file paths (for example `file:///C:/Users/you/video.mp4`) are rejected with `400 Invalid request` — Motion servers cannot read files on your machine. If a file only exists locally, upload it first via `POST /uploads` (see Upload Local Files). If both `design_system_id` and `design_md` are sent, `design_md` content is used. Pro/Max accounts can use `design_system_id` for built-in presets or `design_md` to include custom DESIGN.md file content. Credit requirement: the API key owner's account must have available Mosaic Motion credits. If credits are insufficient, the API can return HTTP 402 with `error: "insufficient_credits"`. Users must add credits or enable auto top-ups in the frontend at https://motion.so. Example request: ```bash curl https://api.motion.so/api/motion/sessions \ -H "Authorization: Bearer motion_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Create a 15 second product launch video for a new AI calendar app.", "aspect_ratio": "16:9", "duration": "10-30s", "design_system_id": "stripe", "style_reference_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "attachments": [ { "url": "https://storage.example.com/signed/hero-image.png", "name": "Hero image", "type": "image", "content_type": "image/png" } ] }' ``` Custom DESIGN.md example: ```bash curl https://api.motion.so/api/motion/sessions \ -H "Authorization: Bearer motion_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Create a premium launch video for a new observability dashboard.", "aspect_ratio": "9:16", "duration": "30s-1min", "design_md": { "filename": "DESIGN.md", "content": "# Design System\n\nUse a black canvas, electric green data accents, dense technical typography, and fast graph-driven transitions." } }' ``` Successful response: ```json { "job_id": "405d47cf-f72b-4852-b570-0de2446dbba1", "status": "queued", "status_url": "https://api.motion.so/api/motion/sessions/405d47cf-f72b-4852-b570-0de2446dbba1", "chat_url": "https://motion.so/agent?chat=405d47cf-f72b-4852-b570-0de2446dbba1" } ``` `status_url` is not a Motion app page. Call it with `Authorization: Bearer motion_...` to poll status and output readiness. Use `chat_url` when a human needs to open the Motion chat. Status code: `202 Accepted`. ## Upload Local Files Endpoint: ```http POST /uploads ``` Full URL: ```txt https://api.motion.so/api/motion/uploads ``` Use this when a file only exists on the local machine. It returns a one-shot signed upload URL plus an `attachment_url` you can pass in the `attachments` array once the upload finishes. Request body: ```json { "filename": "reference.mp4", "content_type": "video/mp4" } ``` Supported content types: video (`video/mp4`, `video/quicktime`, `video/x-msvideo`, `video/webm`, `video/x-matroska`, `video/x-m4v`, max 5 GB), audio (`audio/mpeg`, `audio/mp3`, `audio/wav`, `audio/mp4`, `audio/x-m4a`, `audio/flac`, `audio/ogg`, `audio/aac`, `audio/opus`, `audio/webm`, max 500 MB), image (`image/png`, `image/jpeg`, `image/jpg`, `image/gif`, `image/webp`, `image/svg+xml`, max 50 MB). Response: ```json { "upload_id": "b3c1a2d4-...", "upload_url": "https://storage.googleapis.com/...", "upload_method": "POST", "upload_fields": { "key": "...", "policy": "...", "x-goog-signature": "..." }, "upload_url_expires_in_seconds": 3600, "attachment_url": "https://storage.googleapis.com/...signed...", "attachment_url_expires_in_seconds": 604800, "max_file_size_bytes": 5368709120, "content_type": "video/mp4", "filename": "reference.mp4" } ``` Upload the file bytes with multipart/form-data: include every `upload_fields` entry as a form field, then the file itself as the LAST field named `file`. A `204` response means the upload succeeded. ```bash curl -sS -o /dev/null -w "%{http_code}" "$UPLOAD_URL" \ -F "key=..." -F "policy=..." -F "x-goog-signature=..." \ -F "file=@./reference.mp4" ``` Then pass `attachment_url` in the `attachments` array of `POST /sessions` or `POST /sessions/{job_id}/followups`. The upload URL is valid for 1 hour; `attachment_url` is valid for 7 days. Rate limit: at most 30 uploads per user per 5 hours (HTTP 429 `rate_limited` beyond that). Uploaded files are deleted automatically after 14 days — this flow is for attaching source material to jobs, not general file storage. MCP agents get the same flow through the `upload_asset` tool. ## Create Followup Endpoint: ```http POST /sessions/{job_id}/followups ``` Full URL: ```txt https://api.motion.so/api/motion/sessions/{job_id}/followups ``` Followups use the same body as `POST /sessions`: required `prompt`; optional `aspect_ratio`, `duration`, `design_system_id`, `design_md`, `style_reference_url`; and up to 10 attachments. Followups are only allowed when the current job status is `completed`. If the job is not completed, the API returns HTTP 409 with `error: "followup_requires_completed_job"`. Example request: ```bash curl https://api.motion.so/api/motion/sessions/405d47cf-f72b-4852-b570-0de2446dbba1/followups \ -H "Authorization: Bearer motion_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Make the pacing faster and add a stronger call to action.", "style_reference_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }' ``` Successful response: ```json { "job_id": "405d47cf-f72b-4852-b570-0de2446dbba1", "status": "queued", "status_url": "https://api.motion.so/api/motion/sessions/405d47cf-f72b-4852-b570-0de2446dbba1", "chat_url": "https://motion.so/agent?chat=405d47cf-f72b-4852-b570-0de2446dbba1" } ``` ## Poll Job Endpoint: ```http GET /sessions/{job_id} ``` Full URL: ```txt https://api.motion.so/api/motion/sessions/{job_id} ``` Headers: ```http Authorization: Bearer motion_your_api_key ``` Example request: ```bash curl https://api.motion.so/api/motion/sessions/405d47cf-f72b-4852-b570-0de2446dbba1 \ -H "Authorization: Bearer motion_your_api_key" ``` Successful response: ```json { "job_id": "405d47cf-f72b-4852-b570-0de2446dbba1", "status": "running", "chat_url": "https://motion.so/agent?chat=405d47cf-f72b-4852-b570-0de2446dbba1", "created_at": "2026-04-28T23:01:04.198Z", "updated_at": "2026-04-28T23:01:23.695Z", "completed_at": null, "error": null, "pending_user_input": false, "output": null, "sources": [] } ``` The Mosaic Motion API does not expose a separate public output or download endpoint. Keep polling this status endpoint. When the MP4 is ready, `output.status` is `completed` and `output.download_url` is a signed download URL. The response can also include `sources`, an additive array of source assets currently used in the composition: ```json { "job_id": "405d47cf-f72b-4852-b570-0de2446dbba1", "status": "completed", "chat_url": "https://motion.so/agent?chat=405d47cf-f72b-4852-b570-0de2446dbba1", "created_at": "2026-04-28T23:01:04.198Z", "updated_at": "2026-04-28T23:06:43.695Z", "completed_at": "2026-04-28T23:05:58.695Z", "error": null, "pending_user_input": false, "output": { "status": "completed", "download_url": "https://storage.googleapis.com/...", "expires_at": "2026-04-29T00:06:43.695Z", "completed_at": "2026-04-28T23:06:41.112Z", "error": null }, "sources": [ { "id": "6c1d19dd-b30e-4df2-9d9c-e65598b6d31b", "source_kind": "licensed_library", "provider": "getty", "provider_asset_id": "1234567890", "source_url": "https://www.gettyimages.com/detail/video/example-news-footage/1234567890", "display_url": "https://media.gettyimages.com/...", "source_domain": "gettyimages.com", "media_type": "video", "title": "Example news footage", "credit": "Contributor / Collection", "rights_status": "licensed", "usages": [ { "start_seconds": 0, "end_seconds": 4.5, "duration_seconds": 4.5, "scene_id": "intro", "scene_index": 0, "slot_key": "hero", "element_tag": "video" } ], "source_metadata": {}, "rights_metadata": {} } ] } ``` `sources` is additive and may be empty. `source_kind` categorizes acquisition as `licensed_library`, `article`, `web_search`, `web`, `connector`, `upload`, `generated`, or `unknown`. `provider` identifies the service or origin, such as `getty`, `pexels`, `perplexity`, `github`, or an article domain. `rights_status` is `licensed`, `public`, `restricted`, `user_provided`, `generated`, or `unknown`. Internal storage paths, filenames, and GCS URIs are not exposed. Status code: `200 OK`. ## Job Statuses Possible `status` values: - `queued`: accepted and waiting to start. - `created`: backing Mosaic Motion session exists and is preparing. - `running`: actively generating. - `awaiting_user_input`: waiting on input. API initiated runs auto approve planning stages where possible. - `completed`: finished successfully. - `failed`: failed. Check `error`. Recommended polling interval: 2 to 5 seconds initially, then increase for long-running jobs. ## Errors Errors are JSON. 401 authentication error: ```json { "error": "Mosaic Motion API key authentication required" } ``` 402 insufficient credits error: ```json { "error": "insufficient_credits", "code": "insufficient_credits_minimum_kickoff_balance", "message": "Starting a new Mosaic Motion session requires at least 1 credit in your balance.", "minimum_required_credits": 1, "current_balance": 0 } ``` 400 validation error: ```json { "error": "Invalid request", "details": [ { "code": "too_big", "maximum": 10, "type": "array", "inclusive": true, "message": "Array must contain at most 10 element(s)", "path": ["attachments"] } ] } ``` 404 job not found: ```json { "error": "Job not found" } ``` 409 followup requires completed job: ```json { "error": "followup_requires_completed_job", "message": "Followups can only be created for completed Mosaic Motion jobs.", "status": "running" } ``` ## AI Agents (MCP) and OAuth 2.1 AI agents can connect to Mosaic Motion over the Model Context Protocol (MCP). The MCP server is guarded by an in-process OAuth 2.1 authorization server. Endpoints: ```txt MCP server: https://mcp.motion.so/mcp Protected resource metadata: https://mcp.motion.so/.well-known/oauth-protected-resource (RFC 9728) Authorization server metadata: https://mcp.motion.so/.well-known/oauth-authorization-server (RFC 8414) JWKS: https://mcp.motion.so/oauth/jwks Device verification (human): https://motion.so/device ``` When a client connects with no credential, the MCP endpoint returns HTTP 401 with an RFC 9728 `WWW-Authenticate` challenge, so a compliant MCP client runs the OAuth flow automatically and the user authorizes in the browser — no API key needed. An invalid or expired credential is also rejected with 401. ### Authenticating There are three ways an agent authenticates to the MCP server, in order of preference: 1. Browser authorization (default). The agent registers itself via Dynamic Client Registration and runs the OAuth flow (`authorization_code` + PKCE, or the device flow). The user authorizes in a browser — signing up for Motion if they are new — and approves the requested scopes. The agent receives a token and acts as that user. No API key. 2. Service account (autonomous). An agent registers itself — no human, no device code — when self-service registration is enabled: it POSTs `{ "name": "..." }` to `POST https://mcp.motion.so/oauth/service-accounts/register` (discoverable as `service_account_registration_endpoint` in the authorization-server metadata), receiving a `client_id` and one-time `client_secret`. (A human can also create one via the `register_service_account` tool or the Motion app under Settings → MCP → Let your agent sign itself up.) The agent then authenticates with neither an API key nor a browser sign-in: ```http POST https://mcp.motion.so/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id=...&client_secret=... ``` A service account has its own identity, credits, and billing, separate from any user. Registration returns a `payment_setup_url`: add a card to the agent's own wallet there once (or call the `setup_payment_method` tool for a fresh link). After a card is on file, `purchase_credits` charges it directly (`status: "charged"`, no checkout form) and `set_auto_topup` keeps the agent funded automatically — so it pays entirely on its own. 3. Motion API key (fallback). If browser authorization is unavailable, the user prefers a key, or they are running directly against their account, set a `motion_` key as the connector's bearer token (`Authorization: Bearer motion_...`). The agent receives the full tool catalog and acts as that user. ### OAuth grants Supported grants: `authorization_code` with PKCE (S256), `refresh_token` (rotating, with reuse detection), `client_credentials`, and the device authorization grant `urn:ietf:params:oauth:grant-type:device_code` (RFC 8628). Clients register via Dynamic Client Registration (`POST /oauth/register`, RFC 7591) or CIMD; autonomous agents self-register a service account via `POST /oauth/service-accounts/register` (when enabled). Access tokens are RS256 JWTs whose `aud` is `https://mcp.motion.so/mcp` (RFC 8707). Device flow: ```txt POST /oauth/register -> { client_id } (public client; token_endpoint_auth_method=none) POST /oauth/device_authorization -> { device_code, user_code, verification_uri_complete, interval, expires_in } # show the user verification_uri_complete; they approve at motion.so/device POST /oauth/token grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=...&client_id=... -> 400 authorization_pending (keep polling at interval) -> 400 slow_down (back off) -> 200 { access_token, refresh_token, expires_in, scope } ``` ### Scopes `motion:sessions:read`, `motion:sessions:write`, `motion:account:read`, `motion:account:manage`, `motion:credits:read`, `motion:credits:purchase`, `motion:credits:manage`, `mcp`. Motion API keys have full access. ### Tools - `create_video` — generate a video from a prompt (async; returns `job_id`). Renders the live video widget. - `create_followup` — refine a completed job (same live widget). - `upload_asset` — get a signed upload URL for a local file (video/audio/image). Motion cannot read `file://` paths: call this, POST the file bytes to `upload_url` (every `upload_fields` entry as a form field, then the file as the LAST field named `file`; expect 204), and pass the returned `attachment_url` in `attachments`. - `get_session_status` — re-show a job in the widget; when status is `completed`, read `output.download_url`. - `job_status` — app-only (hidden from the model); the video widget calls it to refresh itself. Returns the generation plus `poll_after_seconds`. - `show_plans_and_credits` — show plans, balance, and top-ups in the plans widget. - `get_credit_balance` — current credit balance and plan. - `list_plans` — available plans and credit top-ups. - `subscribe_to_plan` — start a checkout to subscribe to / switch a plan (`plan_id`, e.g. `motion_pro`, `motion_pro_annual`, `motion_max`, `motion_max_annual`, `motion_flex`). - `purchase_credits` — buy credits (charges a card on file, or returns a `checkout_url`); idempotent on `idempotency_key`. - `setup_payment_method` — get a one-time link to add a card to your wallet, so later top-ups charge on file with no form. - `set_auto_topup` — configure automatic top-ups. - `register_service_account` — create an autonomous agent identity (human subjects only). - `list_api_keys`, `create_api_key`, `revoke_api_key`, `get_settings` — manage Motion API keys and settings. - `whoami` — introspect the current credential. `create_video` accepts the same fields as `POST /sessions`: `prompt` (required, 1-12000 chars), and optional `aspect_ratio` (`16:9`, `9:16`, `1:1`, `4:5`, `21:9`), `duration` (`<10s`, `10-30s`, `30s-1min`, `1-5min`), `design_system_id`, `design_md`, `style_reference_url` (YouTube), and up to 10 `attachments`. ### ChatGPT App Create a ChatGPT app named `Motion` with MCP URL `https://mcp.motion.so/mcp`. The server supports streaming HTTP, OAuth 2.1, Dynamic Client Registration, and Client ID Metadata Documents. ChatGPT can render the same Motion MCP Apps widgets used by other compatible hosts. ChatGPT app guide: https://docs.motion.so/guides/chatgpt-app ### Interactive widgets (MCP Apps) In hosts that support MCP Apps (including ChatGPT Apps and Claude), Motion tools render interactive UI inline. Widgets are served as `ui://motion/*.html` resources with mimeType `text/html;profile=mcp-app`; a tool opts a widget in via `_meta.ui.resourceUri` and the ChatGPT alias `_meta["openai/outputTemplate"]`, and the host delivers the tool result's `structuredContent` to the widget over the MCP Apps postMessage bridge. - Video widget (`create_video`, `create_followup`, `get_session_status`): shows the job and updates itself live as the video renders, then plays the result inline with a Download button. It polls the app-only `job_status` tool on the server-supplied `poll_after_seconds` cadence, so the agent issues one call and never polls. - Plans widget (`show_plans_and_credits`): plan cards (monthly/annual with annual savings), credit top-ups, auto top-up, and one-click checkout — buttons call `subscribe_to_plan` / `purchase_credits` / `setup_payment_method` and open the Stripe checkout URL. - Out-of-credits recovery: when a generation can't run for lack of credits, the tool returns an error whose `structuredContent.recovery_tool` is `show_plans_and_credits` (with `recovery_tool_args`); the agent opens the plans widget, then retries. Widgets are progressive enhancement: hosts without MCP Apps support receive the same data as plain JSON, and every tool works headlessly. ### Buying credits `purchase_credits` tops up `motion_credits` through Stripe. If a payment method (such as a Stripe Link wallet) is on file, the charge completes immediately and returns `status: "charged"`; otherwise it returns a hosted Stripe Checkout URL with `requires_checkout: true` and `checkout_url`. `subscribe_to_plan` starts a plan subscription the same way (a new subscriber gets a `checkout_url`; an existing paid subscriber switching plans is charged in place). Full MCP guide: https://docs.motion.so/guides/mcp ChatGPT app guide: https://docs.motion.so/guides/chatgpt-app ## Important Behavior - All API initiated jobs are owned by the API key owner. - API jobs require Mosaic Motion credits on the API key owner's account. - Credit purchases and auto top-up settings must be managed in the frontend at https://motion.so, not through the API. - A Mosaic Motion API key cannot poll another user's job. - A Mosaic Motion API key is not valid for non-Mosaic Motion backend routes. - Attachment URLs must be public to Motion servers without extra headers or cookies. Local `file://` paths are rejected; upload local files via `POST /uploads` (API) or `upload_asset` (MCP) first. - YouTube style references must be YouTube URLs. - The public API intentionally exposes only job creation, completed-job followups, and polling. - Followups are only allowed on completed jobs.