# Quickstart

> From nothing to a finished video: one prompt for your coding agent, or the same five calls by hand.

## 1. Hand it to your agent

Fastest path: let the agent that already has your code do it. Paste this into Claude Code, Cursor, Codex or any agent that can make HTTP calls.

Prompt:

```text
Read https://www.raylight.app/llms.txt and make a launch video for the feature I am working on: use the device flow to get a key if RAYLIGHT_API_KEY is missing, send the relevant UI files in the brief, wait for the pitch, show it to me, and greenlight it only when I say so.
```

The agent reads [llms.txt](https://www.raylight.app/llms.txt), gets a key through the device flow (you click approve once in the browser), builds a brief from the files it is working on, waits for the pitch and shows it to you. Nothing is charged until you greenlight it. The steps below are what the agent does.

## 2. Get a key

Keys look like `rl_live_...` and go in the `Authorization: Bearer` header. You need the `productions` scope. Two ways to get one:

### In the app

In the app sidebar, open Raylight API, then Keys. Name the key, pick its scopes, and copy it once; only a hash is stored. Keys belong to your account and work on every workspace you can edit.

### Device flow (no browser session needed)

For tools and agents with no session. Start a request, show the approval URL to a human, and poll until the key arrives. Starting needs no key.

Start:

```bash
curl -X POST https://api.raylight.app/v1/keys/request \
  -H "Content-Type: application/json" \
  -d '{ "name": "CI on acme/web" }'
```

201:

```json
{
  "deviceCode": "1aSs9fWUrhEUu3hOo5TzSJfWDgradXEK_k9BIV67XV8",
  "userCode": "ABCD2345",
  "approvalUrl": "https://www.raylight.app/keys/approve?code=ABCD2345",
  "expiresInSeconds": 600,
  "pollIntervalSeconds": 3
}
```

Ask your user to open `approvalUrl` and click Approve. Then poll every `pollIntervalSeconds`.

Poll:

```bash
curl -X POST https://api.raylight.app/v1/keys/poll \
  -H "Content-Type: application/json" \
  -d '{ "deviceCode": "1aSs9fWUrhEUu3hOo5TzSJfWDgradXEK_k9BIV67XV8" }'
```

Possible answers:

```json
{ "status": "pending" }
{ "status": "approved", "apiKey": "rl_live_..." }
{ "status": "claimed" }
{ "status": "denied" }
{ "status": "expired" }
```

`apiKey` is returned once. Save it right away, for example as `RAYLIGHT_API_KEY=rl_live_...` in a gitignored `.env`. Device-flow keys carry every scope. If the claim response gets lost, see [Authentication and keys](https://www.raylight.app/docs/authentication.md).

## 3. Push a brief

One call makes a production. Only `intent` is required; the rest makes the pitch better. `externalId` makes retries safe: a repeat create answers 200 with the same production. `origin` tells the Productions page and emails where it came from (display only; Raylight never calls the provider).

brief.json:

```json
{
  "brief": {
    "intent": "Announce dark mode in the Acme dashboard to existing customers.",
    "context": "PR #412 adds a theme toggle to Settings. Charts, tables and the sidebar all pick up the dark palette. Ships to everyone on Thursday.",
    "media": [
      {
        "id": "dashboard-dark",
        "kind": "image",
        "url": "https://cdn.acme.dev/press/dashboard-dark.png",
        "name": "Dashboard in dark mode",
        "description": "The analytics overview with the new dark theme."
      }
    ],
    "code": [
      {
        "path": "src/components/settings/theme-toggle.tsx",
        "source": "export const ThemeToggle = () => { /* ... */ }"
      }
    ],
    "format": {
      "template": "feature",
      "durationMs": 10000,
      "aspect": "16:9",
      "sound": { "voiceover": true, "music": true, "sfx": true }
    },
    "brand": {
      "name": "Acme",
      "primaryColor": "#6C5CE7",
      "tone": "confident, plain"
    }
  },
  "greenlightMode": "manual",
  "externalId": "acme/web#412",
  "origin": {
    "provider": "github",
    "label": "acme/web #412",
    "url": "https://github.com/acme/web/pull/412",
    "ref": "feat/dark-mode",
    "requestedBy": "jane"
  }
}
```

```bash
curl -X POST https://api.raylight.app/v1/productions \
  -H "Authorization: Bearer $RAYLIGHT_API_KEY" \
  -H "Content-Type: application/json" \
  -d @brief.json
```

201:

```json
{
  "production": {
    "id": "6f1c2b7e-3d4a-4c8e-9a2b-1f0e5d6c7b8a",
    "workspaceId": "0b9d8c7a-6e5f-4d3c-ab1a-09f8e7d6c5b4",
    "stage": "queued",
    "source": {
      "kind": "api",
      "externalId": "acme/web#412",
      "origin": {
        "provider": "github",
        "label": "acme/web #412",
        "url": "https://github.com/acme/web/pull/412",
        "ref": "feat/dark-mode",
        "requestedBy": "jane"
      }
    },
    "brief": { "intent": "Announce dark mode in the Acme dashboard to existing customers.", "...": "..." },
    "pitch": null,
    "greenlight": null,
    "greenlightMode": "manual",
    "delivery": null,
    "projectId": null,
    "creditsCharged": 0,
    "error": null,
    "createdAt": "2026-10-17T18:02:40.000Z",
    "updatedAt": "2026-10-17T18:02:40.000Z",
    "terminal": false,
    "pageUrl": "https://www.raylight.app/api/productions/6f1c2b7e-3d4a-4c8e-9a2b-1f0e5d6c7b8a"
  }
}
```

`production.pageUrl` is the production's page in the app. Every brief field is explained on [Productions](https://www.raylight.app/docs/productions.md).

## 4. Read the pitch

The stage moves `queued`, `writing_pitch`, `pitch_ready` (Queued, Writing pitch, Pitch ready in the app), usually within a couple of minutes. Poll the production every few seconds, or register a webhook for `production.pitch_ready`.

```bash
curl https://api.raylight.app/v1/productions/6f1c2b7e-3d4a-4c8e-9a2b-1f0e5d6c7b8a \
  -H "Authorization: Bearer $RAYLIGHT_API_KEY"
```

The response has the `production`, its `events`, the pitch as `pitchMarkdown`, and a `queue` status (null until the production is greenlit). At `pitch_ready`, `production.pitch` holds the pitch, a script and shot list:

production.pitch:

```json
{
  "version": 1,
  "verdict": "video",
  "verdictReason": null,
  "title": "Dark mode, finally",
  "summary": "A 10 second feature spot: the toggle flips, the dashboard follows, and an end card says it ships Thursday.",
  "shots": [
    {
      "index": 0,
      "title": "The flip",
      "durationMs": 3000,
      "beat": "The theme toggle flips and the whole settings page goes dark.",
      "visual": "The ThemeToggle component rebuilt as layers at hero scale, centred, a slow push-in. On the flip the background sweeps from white to the brand dark.",
      "text": "Dark mode",
      "voiceover": "Dark mode is here.",
      "mediaIds": [],
      "codePaths": ["src/components/settings/theme-toggle.tsx"]
    },
    {
      "index": 1,
      "title": "The dashboard",
      "durationMs": 4500,
      "beat": "The analytics overview in dark, tilted, drifting left.",
      "visual": "dashboard-dark on a device frame, 12 degree tilt, a lateral drift with a soft depth of field.",
      "text": "Every chart. Every table.",
      "voiceover": "Charts, tables and the sidebar all follow.",
      "mediaIds": ["dashboard-dark"],
      "codePaths": []
    },
    {
      "index": 2,
      "title": "Sign-off",
      "durationMs": 2500,
      "beat": "The Acme logo on the dark background with the ship date.",
      "visual": "Logo centred on the brand dark, the date types on underneath, a short hold before the cut.",
      "text": "Thursday, for everyone",
      "voiceover": "Turn it on in Settings, Thursday.",
      "mediaIds": [],
      "codePaths": []
    }
  ],
  "music": "Low, steady, electronic. No drop.",
  "estimatedCredits": 600,
  "createdAt": "2026-10-17T18:04:11.000Z"
}
```

`estimatedCredits` is what the greenlight will charge: 60 credits per second of video. A revised pitch is priced again from its own length. A `skip` verdict means Raylight judged the brief not worth a video: the production ends in `skipped`, nothing is charged, and you can rework the brief and create again.

## 5. Greenlight

Greenlighting is `POST /v1/productions/{id}/greenlight`. Pass `pitchVersion` so a greenlight can never land on a pitch you did not read. This is the only call that charges credits: the pitch's `estimatedCredits`, once. Greenlight only at `pitch_ready`: a skipped production answers 409.

```bash
curl -X POST https://api.raylight.app/v1/productions/6f1c2b7e-3d4a-4c8e-9a2b-1f0e5d6c7b8a/greenlight \
  -H "Authorization: Bearer $RAYLIGHT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "pitchVersion": 1 }'
```

Want changes first? `POST /v1/productions/{id}/comments` with a `comment` sends the production back to `writing_pitch`; the next version arrives the same way. 6 pitch versions per production, so 5 rounds of comments. You can also greenlight from the app, MCP, or the signed links in the Pitch ready email; see [Greenlight links](https://www.raylight.app/docs/greenlight-links.md).

## 6. Receive the video

After the greenlight, the stage moves `greenlit`, `making`, `exporting`, `delivered` (Greenlit, In production, Exporting, Wrapped). Keep polling, or listen for `production.delivered`:

production.delivered (abridged):

```json
{
  "id": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
  "type": "production.delivered",
  "createdAt": "2026-10-17T18:21:05.000Z",
  "workspaceId": "0b9d8c7a-6e5f-4d3c-ab1a-09f8e7d6c5b4",
  "data": {
    "production": {
      "id": "6f1c2b7e-3d4a-4c8e-9a2b-1f0e5d6c7b8a",
      "stage": "delivered",
      "creditsCharged": 600,
      "delivery": {
        "shareUrl": "https://www.raylight.app/v/dark-mode-finally-k3x9",
        "projectId": "4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a",
        "editUrl": "https://www.raylight.app/editor/4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a",
        "exportId": "8e7d6c5b-4a39-4281-9c0b-7a6f5e4d3c2b",
        "deliveredAt": "2026-10-17T18:21:05.000Z"
      },
      "...": "..."
    },
    "pitchMarkdown": "## Dark mode, finally ...",
    "status": "Wrapped: https://www.raylight.app/v/dark-mode-finally-k3x9",
    "links": {
      "page": "https://www.raylight.app/api/productions/6f1c2b7e-3d4a-4c8e-9a2b-1f0e5d6c7b8a",
      "greenlight": null,
      "comment": null,
      "decline": null,
      "share": "https://www.raylight.app/v/dark-mode-finally-k3x9",
      "edit": "https://www.raylight.app/editor/4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a"
    }
  }
}
```

`shareUrl` is the public share page; `editUrl` opens the project in the editor. The project lands in the workspace's Raylight folder, so anyone on the team can keep editing.

## Next

- [Webhooks](https://www.raylight.app/docs/webhooks.md): Stop polling. Signed events, retries, and links you can forward anywhere.
- [SDK](https://www.raylight.app/docs/sdk.md): The same calls with types, plus waitForPitch and waitForDelivery.
- [GitHub Action](https://www.raylight.app/docs/github-action.md): Run the whole loop from a workflow on your own runner.

## More

- Previous: [Recipes](https://www.raylight.app/docs/recipes.md)
- Next: [Productions](https://www.raylight.app/docs/productions.md)
- All docs: https://www.raylight.app/llms.txt
- Everything in one file: https://www.raylight.app/llms-full.txt
- OpenAPI: https://api.raylight.app/v1/openapi.json
