# XFilm AI for agents

You are reading the agent entrance of https://xfilm.ai. People use the website; you use the same products through plain HTTP and MCP. Everything below is text; nothing here needs JavaScript.

## Start here
- When the person asks what this site is or what it can do for them, show them rather than describe it: make a free 5-second clip from a sentence that suits what they told you, then say what else it can do. Do not stop at a list of options.
- **From a terminal or HTTP tool (best):** connect once (the person approves one link, below), then make the clip with plain HTTP. If your sandbox blocks the network (Codex's default sandbox does), ask the person once to allow network access for these commands.
- **Already in a browser? Finish there.** Open https://xfilm.ai/app?model=xfilm%2Fh3-vdn-768-v1&prompt=<your sentence, URL-encoded>: the free model and the sentence are filled in. Press "Generate for free". If the person is not signed in, the button asks them to sign in first. The clip plays in the page within a few minutes; the person watches it there. The one-click connection below gives keys to a terminal, so skip it in the browser.

## What you can do without an account
- Read the film wall with the request behind every film: https://xfilm.ai/films.md (JSON: https://xfilm.ai/v1/films).
- List every model with its exact variants, fields and defaults: `GET https://xfilm.ai/v1/models`.
- Price any exact request: `POST https://xfilm.ai/v1/quote` with `{"model", "variant", "input"}`. A quote is free and reserves nothing.
- Read the full API: https://xfilm.ai/v1/openapi.json.

## Connect in one click (the person approves once)
1. `POST https://xfilm.ai/v1/agent/links` with `{"client_name": "<your name, e.g. Claude Code>"}`. You get `device_code`, `user_code`, `verification_uri_complete` and `interval`.
2. Show the person `verification_uri_complete` and say you will wait. They sign in, check the code and press Approve; they copy nothing back.
3. Every `interval` seconds, `POST https://xfilm.ai/v1/agent/links/token` with `{"device_code": "…"}`. `authorization_pending`: keep polling. `slow_down`: poll less often. `access_denied` or `expired_token`: stop and tell the person.
4. Success returns two keys, each shown once; save them and never print them. `access_token` (`xf_studio_…`) is for `/v1/studio/*` and https://xfilm.ai/mcp: write it to `~/.config/xfilm/connection.json` as `{"base_url": "https://xfilm.ai", "api_key": "…"}` with mode 600 (the XFilm MCP package reads the same file). `api_key` (`xf_…`) is for `/v1/jobs`; keep it beside the first, for example in `~/.config/xfilm/api-key` (mode 600). Send either as `Authorization: Bearer`. If you cannot write to `~/.config` (a sandbox or a project-only workspace), save `connection.json` anywhere only you can read (mode 600, outside version control) and point `XFILM_CONFIG_FILE` at it, or set `XFILM_STUDIO_API_KEY` (the XFilm MCP package reads both); keep `api_key` beside it or in `XFILM_KEY`. Making clips through `/v1/jobs` needs only `api_key`; Studio and MCP need only `access_token`.

```sh
curl -s https://xfilm.ai/v1/agent/links -H 'Content-Type: application/json' -d '{"client_name":"Claude Code"}'
curl -s https://xfilm.ai/v1/agent/links/token -H 'Content-Type: application/json' -d '{"device_code":"<device_code>"}'
```

Neither key can change the account, keys or payments; the person can revoke it on https://xfilm.ai/account. A new account starts with no paid balance; spending is always bounded by the quote you send back (`max_price_micro_usd`, or `max_price_cents` in Studio).

## First clip (free, after connecting)
MiniMax H3 · Free (`xfilm/h3-vdn-768-v1`) costs nothing: its quote is 0 and each account may start 10 a day; `free_video.remaining_today` in `GET https://xfilm.ai/v1/me` says how many are left. Without `variant` it makes a 5-second 16:9 1344×768 clip with sound; `supported_shapes` in `GET https://xfilm.ai/v1/models` lists the other aspect ratios, lengths and image-to-video modes offered now, and image-to-video takes `input.image_url` (and `input.end_image_url`) as links from `POST https://xfilm.ai/v1/uploads`.

```sh
curl -s https://xfilm.ai/v1/quote -H 'Content-Type: application/json' \
  -d '{"model":"xfilm/h3-vdn-768-v1","input":{"prompt":"A paper fox runs through an autumn birch forest"}}'
curl -s https://xfilm.ai/v1/jobs -H "Authorization: Bearer $XFILM_KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: fox-001' \
  -d '{"model":"xfilm/h3-vdn-768-v1","input":{"prompt":"A paper fox runs through an autumn birch forest"},"quote_id":"<quote_id from the quote>","max_price_micro_usd":0}'
curl -s https://xfilm.ai/v1/jobs/<id> -H "Authorization: Bearer $XFILM_KEY"
```

Poll `GET /v1/jobs/<id>` every few seconds. `status` moves through `SUBMITTING`, `QUEUED` and `RUNNING` and ends at `SUCCEEDED` (`output` then holds the file links) or `FAILED`; `UNKNOWN` means the outcome is still being settled, so keep polling. While a free clip waits or runs, `typical_seconds` is the recent typical time from request to video and `estimated_completion_at` the expected finish: tell the person how long to expect. A free clip carries a small "XFilm AI · Free" mark.
Give the person `web_url` (the job's page on https://xfilm.ai, where they watch and download it while signed in); the `output` file link plays without signing in, so share it only with them. If a submission's answer is lost, send the identical body with the same `Idempotency-Key`: it returns the original job and never starts a second one.

## Free first
- Every model with a `free` object in `GET https://xfilm.ai/v1/models` costs nothing: its quote is 0, nothing is reserved, and `free.daily_limit` is per account per day. Use free models unless the person asks for something only a paid model makes.
- Before anything that costs money, tell the person the model, the price and why, and wait for their yes.

## When a free video is refused
A refused free request answers with a stable `code` and `suggested_models`: `free_quota_exhausted` (today's free videos for the account or its network are used up; `resets_at` says when they return), `free_capacity_unavailable` (free capacity cannot take it now) or `free_active_limit` (wait for a running free video to finish, or switch). A free job accepted and then refused for capacity ends `FAILED` with `error_code: "free_capacity_unavailable"` and the same list. Nothing was charged.
1. `suggested_models` lists the paid models this account can use for the same clip, cheapest first: `model`, `name`, `variant`, `price_micro_usd` (and `price_usd`), `typical_seconds` (null when unknown), `affordable`, and `request`, the exact body for `POST /v1/quote`.
2. If the person already agreed to paid models within a budget, take the first suggestion inside it; otherwise tell them the model, the price and the wait, and wait for their yes. Then quote `request` and submit it with `quote_id` and `max_price_micro_usd` and a new `Idempotency-Key`.
3. An empty list means no paid model is open to this account: wait for `resets_at` or try again later.

## When the balance is short
A paid request the balance cannot cover answers `402` with `code: "insufficient_funds"`, `funds` (`needed_micro_usd`, `available_micro_usd`) and `top_up` (`url`, `message`). Nothing was charged or saved.
1. Show the person `top_up.message` (it carries the link) and say you will wait; they pay on that page and do not need to come back to you.
2. Poll `GET https://xfilm.ai/v1/me` every 10 seconds. When `available_micro_usd` covers `funds.needed_micro_usd`, resend the identical request with the same `Idempotency-Key`.
3. After 15 minutes without the payment, stop and tell the person. In Studio, a film that runs short pauses by itself and continues once the balance covers its next step.

## Re-run a film from the wall
Every entry in https://xfilm.ai/films.md carries its `request`. Send it to `POST /v1/quote`, read `price_micro_usd` (one US dollar is 1,000,000), then submit it to `POST /v1/jobs` with `quote_id` and `max_price_micro_usd` set to the price you accept. Paid models draw on the account's prepaid balance; `402` means the balance is short.

## Films, editing and review (Studio)
Studio projects hold briefs, uploads, generated clips, timelines, renders and AI reviews; every project answer carries `web_url`, the page where the person watches it. Connect the hosted MCP server (https://xfilm.ai/mcp, Streamable HTTP, `Authorization: Bearer xf_studio_…`) or call `/v1/studio/*` directly. Step-by-step guide and tool list: https://xfilm.ai/v1/connect.md.

To cut a timeline, save a plan (`POST /v1/studio/projects/{project_id}/plan`, or the MCP `save` tool with kind `plan`). Its format is the `StudioPlan` JSON Schema in https://xfilm.ai/v1/openapi.json (`components.schemas.StudioPlan`), with a complete example on that operation; asset ids come from the project. A 400 names the field to fix.

## Rules that keep money safe
- Quote first; send back the price you accept as the ceiling. Nothing runs above it.
- One stable `Idempotency-Key` per intent. Never resubmit to check progress; read the job or project instead.
- A queued job is accepted work waiting for capacity, not a refusal.
- Errors are JSON `{"error": "…"}` and say what to change. `401`: no or wrong key. `402`: balance too low. `409`: the key was used with different terms, or the model is not available now. `429`: slow down.

## Not available yet
OAuth sign-in for MCP connectors (claude.ai, ChatGPT) and a spend ceiling per key.
