npm package: @remotion-studio/sdk
Description: Official Remotion Studio package for generating, editing, playing, and rendering AI-powered Remotion compositions.
Latest version: 0.1.4

# @remotion-studio/sdk

Official SDK for the hosted Remotion Studio API.

This package includes:

- `@remotion-studio/sdk` for calling the API
- `@remotion-studio/sdk/runtime` for rendering returned artifacts inside Remotion or `@remotion/player`

## Install

```bash
npm install @remotion-studio/sdk
```

```bash
pnpm add @remotion-studio/sdk
```

```bash
bun add @remotion-studio/sdk
```

## Authentication

Create a personal API token in the Remotion Studio web app, then use it as a bearer token.

```ts
import { createRemotionStudioClient } from "@remotion-studio/sdk";

const client = createRemotionStudioClient({
  baseUrl: "https://remotionstudio.io",
  headers: {
    Authorization: `Bearer ${process.env.REMOTIONSTUDIO_API_TOKEN}`,
  },
});
```

The plaintext token is shown only once when it is created.

## Client Options

`createRemotionStudioClient()` accepts:

- `baseUrl?: string`
- `headers?: HeadersInit`
- `fetch?: typeof fetch`

`baseUrl` should be the Remotion Studio origin, for example `https://remotionstudio.io`, not `https://remotionstudio.io/api`.

## SDK Methods

| Method                 | HTTP   | Endpoint                     | Purpose                                         |
| ---------------------- | ------ | ---------------------------- | ----------------------------------------------- |
| `generate()`           | `POST` | `/api/generate`              | Generate a composition                          |
| `generateStream()`     | `POST` | `/api/generate`              | Stream generation output with `streaming: true` |
| `edit()`               | `POST` | `/api/edit`                  | Edit existing code                              |
| `generateAsync()`      | `POST` | `/api/generate/async`        | Queue a generation job                          |
| `getGenerationJob()`   | `GET`  | `/api/generate/async/:jobId` | Read generation job state                       |
| `pollGenerationJob()`  | `GET`  | `/api/generate/async/:jobId` | Wait for generation completion                  |
| `editAsync()`          | `POST` | `/api/edit/async`            | Queue an edit job                               |
| `getEditJob()`         | `GET`  | `/api/edit/async/:jobId`     | Read edit job state                             |
| `pollEditJob()`        | `GET`  | `/api/edit/async/:jobId`     | Wait for edit completion                        |
| `render()`             | `POST` | `/api/render/render`         | Start a Remotion Lambda render                  |
| `getRenderProgress()`  | `POST` | `/api/render/progress`       | Fetch render progress                           |
| `pollRenderProgress()` | `POST` | `/api/render/progress`       | Wait for render completion                      |

## Generate

```ts
const generated = await client.generate({
  prompt: "A cinematic product hero reveal with dramatic lighting",
  reasoningEffort: "none",
});
```

Request fields:

- `prompt: string`
- `presetId?: string`
- `reasoningEffort?: "none" | "low" | "medium" | "high" | "xhigh"`
- `frameImages?: string[]`

## Edit

```ts
const edited = await client.edit({
  prompt: "Make it faster, cleaner, and more typography-driven",
  currentCode: generated.code,
  threadId: generated.thread?.id,
});
```

Common request fields:

- `prompt: string`
- `currentCode: string`
- `threadId?: string`
- `conversationHistory?: { role: "user" | "assistant"; content: string; attachedImages?: string[] }[]`
- `hasManualEdits?: boolean`
- `errorCorrection?: { error: string; source?: "compilation" | "runtime" | "generation"; attemptNumber: number; maxAttempts: number; failedEdit?: { description: string; old_string: string; new_string: string; lineNumber?: number } }`
- `previouslyUsedSkills?: string[]`
- `frameImages?: string[]`
- `reasoningEffort?: "none" | "low" | "medium" | "high" | "xhigh"`

## Stream Generation

```ts
const result = await client.generateStream(
  {
    prompt: "A kinetic typography intro",
  },
  {
    onMetadata: (metadata) => {
      console.log(metadata);
    },
    onReasoningStart: () => {
      console.log("reasoning started");
    },
    onReasoning: (reasoning) => {
      console.log(reasoning);
    },
    onTextStart: () => {
      console.log("code started");
    },
    onCode: (partialCode) => {
      console.log(partialCode);
    },
  },
);
```

## Async Jobs

Use async methods for long-running generations or edits.

### Queue A Generation

```ts
const job = await client.generateAsync({
  prompt: "A premium SaaS launch video opener",
  reasoningEffort: "high",
  webhook: {
    url: "https://your-app.example.com/remotionstudio/webhook",
    headers: {
      Authorization: "Bearer your-app-owned-callback-token",
    },
  },
});

const generated = await client.pollGenerationJob(job.id, {
  intervalMs: 2000,
  timeoutMs: 10 * 60 * 1000,
});
```

### Read A Generation Job

```ts
const jobState = await client.getGenerationJob(job.id);
```

### Queue An Edit

```ts
const editJob = await client.editAsync({
  prompt: "Add more contrast and speed up the transitions",
  currentCode: generated.code,
  threadId: generated.thread?.id,
});

const edited = await client.pollEditJob(editJob.id);
```

### Read An Edit Job

```ts
const editJobState = await client.getEditJob(editJob.id);
```

Webhook headers are caller-owned. Remotion Studio forwards them when delivering the callback.

## Render

Start a render:

```ts
const render = await client.render({
  inputProps: {
    code: generated.code,
    durationInFrames: generated.artifact.composition.durationInFrames,
    fps: generated.artifact.composition.fps,
  },
  threadId: generated.thread?.id,
});
```

Check progress once:

```ts
const progress = await client.getRenderProgress({
  id: render.renderId,
  bucketName: render.bucketName,
});
```

Poll until done:

```ts
const completed = await client.pollRenderProgress(
  {
    id: render.renderId,
    bucketName: render.bucketName,
  },
  {
    intervalMs: 2000,
    timeoutMs: 10 * 60 * 1000,
  },
);

console.log(completed.url);
```

## Responses

`generate()` and `edit()` return:

- `code`
- `summary`
- `metadata`
- `artifact`
- `thread?`

The `artifact` is the stable runtime contract for playback and rendering.

## Runtime

`@remotion-studio/sdk/runtime` exports:

- `MotionGraphicsSlot`
- `MotionGraphicsComposition`
- `calculateMotionGraphicsMetadata`
- `compileMotionGraphicsArtifact`

The runtime executes generated code inside your app process. Treat returned artifacts as trusted code only.

### Render Inside An Existing Composition

Use `MotionGraphicsSlot` when you want to place a generated artifact inside your own composition tree.

```tsx
import { AbsoluteFill } from "remotion";
import {
  MotionGraphicsSlot,
  type MotionGraphicsArtifact,
} from "@remotion-studio/sdk/runtime";

export const MyComposition = ({
  artifact,
}: {
  artifact: MotionGraphicsArtifact;
}) => {
  return (
    <AbsoluteFill>
      <MotionGraphicsSlot artifact={artifact} />
    </AbsoluteFill>
  );
};
```

### Render In `@remotion/player`

```tsx
import { Player } from "@remotion/player";
import {
  MotionGraphicsComposition,
  type MotionGraphicsArtifact,
} from "@remotion-studio/sdk/runtime";

export const ArtifactPlayer = ({
  artifact,
}: {
  artifact: MotionGraphicsArtifact;
}) => {
  return (
    <Player
      component={MotionGraphicsComposition}
      inputProps={{ artifact }}
      durationInFrames={artifact.composition.durationInFrames}
      fps={artifact.composition.fps}
      compositionWidth={artifact.composition.width}
      compositionHeight={artifact.composition.height}
      controls
    />
  );
};
```

### Register As A Remotion Composition

Use `MotionGraphicsComposition` with `calculateMotionGraphicsMetadata` when the artifact should define the composition dimensions and timing.

```tsx
import { Composition } from "remotion";
import {
  MotionGraphicsComposition,
  calculateMotionGraphicsMetadata,
  type MotionGraphicsCompositionProps,
} from "@remotion-studio/sdk/runtime";

export const RemotionRoot = ({ artifact }: MotionGraphicsCompositionProps) => {
  return (
    <Composition
      id="GeneratedComposition"
      component={MotionGraphicsComposition}
      defaultProps={{ artifact }}
      durationInFrames={180}
      fps={30}
      width={1920}
      height={1080}
      calculateMetadata={calculateMotionGraphicsMetadata}
    />
  );
};
```

### Runtime Props

Both `MotionGraphicsSlot` and `MotionGraphicsComposition` accept:

- `artifact: MotionGraphicsArtifact`
- `componentProps?: Record<string, unknown>`

`componentProps` is forwarded to the generated component at runtime.

## Runtime Dependencies

If you only use the API client from `@remotion-studio/sdk`, you do not need any runtime dependencies.

If you use `@remotion-studio/sdk/runtime`, the package installs its runtime helper dependencies for you.

You still need to use it inside a normal React + Remotion app, and install `@remotion/player` separately if you want to use the player example above.

## Errors

API failures throw `RemotionStudioError`.

Useful fields:

- `message`
- `type`
- `status?`
- `failedEdit?`
- `availableCredits?`
- `requiredCredits?`

## API Docs

- Hosted API reference: [remotionstudio.io/api-docs](https://remotionstudio.io/api-docs)

## Package Exports

```ts
import {
  createRemotionStudioClient,
  RemotionStudioError,
} from "@remotion-studio/sdk";
import {
  MotionGraphicsSlot,
  MotionGraphicsComposition,
  calculateMotionGraphicsMetadata,
  compileMotionGraphicsArtifact,
} from "@remotion-studio/sdk/runtime";
```