> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hooked.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Reddit Story Example

> Turn a story idea into a narrated social post video

## Overview

A Reddit story video opens on a social post card and a voice reads the story over a background. You describe the thread you want in `prompt`; AI writes the post and its comments, a voice narrates them, and the video plays over gameplay footage (the default), AI images, AI video clips or your own media.

The card can imitate a Reddit, Facebook or Instagram post (`platform`). It only evokes the layout of each network with Hooked's own shapes and icons, not their logos.

Every request needs `prompt` (up to 4,000 characters) and `voiceId` (from `GET /v1/voice/list`). `targetDuration` is 60 seconds by default (10 to 600).

## Quick Example

```javascript theme={null}
const response = await fetch('https://api.hooked.so/v1/project/create/reddit-story', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.HOOKED_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    prompt: 'A passenger on a long flight refuses to switch seats so a couple can sit together, and the comments argue about who was right.',
    voiceId: '1000',
    webhook: 'https://yoursite.com/webhook?token=YOUR_SECRET'
  })
});

const data = await response.json();
console.log('Project ID:', data.data.projectId);
```

Without `mediaType` the story plays over gameplay footage (`subway-s` / `subway-s-1`). The create call answers with the `projectId`; poll `GET /v1/project/{projectId}` or pass a `webhook` to get the finished video.

## Complete Example

```javascript theme={null}
const createRedditStory = async () => {
  const response = await fetch('https://api.hooked.so/v1/project/create/reddit-story', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.HOOKED_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Seat Swap Story',
      prompt: 'A passenger on a long flight refuses to switch seats so a couple can sit together, and the comments argue about who was right.',
      targetDuration: 90,
      voiceId: '1000',
      audio: {
        speed: 1.1,
        stability: 0.5,
        similarityBoost: 0.75
      },
      mediaType: 'gameplay',
      gameplaySettings: {
        selectedGame: 'minecraft',
        selectedVideo: 'minecraft-4'
      },
      platform: 'reddit',
      narrationScope: 'post_and_comments',
      showCard: true,
      cardTheme: 'dark',
      showAuthor: true,
      cardDurationSeconds: 6,
      aspectRatio: 'ratio_9_16',
      caption: {
        preset: 'wrap1',
        alignment: 'bottom',
        disabled: false
      },
      language: 'en',
      musicId: '1',
      webhook: 'https://yoursite.com/webhook?token=YOUR_SECRET',
      metadata: { series: 'travel-stories' }
    })
  });

  return await response.json();
};
```

## Your Own Card

By default the card shows the post AI wrote. Send `card` to replace any of its fields with your own values; the ones you leave out are still generated.

```javascript theme={null}
const createWithCustomCard = async () => {
  const response = await fetch('https://api.hooked.so/v1/project/create/reddit-story', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.HOOKED_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      prompt: 'Someone finds out their roommate has been secretly using their streaming account to run a movie night for the whole building.',
      voiceId: '1000',
      platform: 'instagram',
      cardTheme: 'light',
      card: {
        author: 'movienightdrama',
        title: 'My roommate turned my account into a building cinema',
        upvotes: 18400,
        commentCount: 932,
        verified: true
      },
      mediaType: 'ai-images',
      presetSettings: {
        preset: 'cinematic'
      },
      webhook: 'https://yoursite.com/webhook?token=YOUR_SECRET'
    })
  });

  return await response.json();
};
```

## Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `prompt` | string | Yes | The story you want (max 4,000 characters) |
| `voiceId` | string | Yes | Narrator voice from `/v1/voice/list` |
| `targetDuration` | number | No | Seconds, 10 to 600. Default 60 |
| `audio` | object | No | Narrator voice settings: `speed`, `stability`, `similarityBoost` |
| `mediaType` | string | No | `gameplay` (default), `ai-images`, `ai-videos` or `media` |
| `gameplaySettings` | object | No | `gameplay`: `{ selectedGame, selectedVideo }`. Default `subway-s` / `subway-s-1` |
| `presetSettings` | object | No | `ai-images` and `ai-videos`: `preset` (default `realistic`), `aiModel`; for `ai-videos` also `quality` (`base`, `pro` or `ultra`) and `isContinuous` (default `true`) |
| `media` | string\[] | `media` only | Library media IDs (max 50) |
| `platform` | string | No | The card: `reddit` (default), `facebook` or `instagram` |
| `narrationScope` | string | No | `post_and_comments` (default) or `comments_only` |
| `showCard` | boolean | No | Show the opening card. Default `true` |
| `cardTheme` | string | No | With the card: `dark` (default) or `light` |
| `showAuthor` | boolean | No | With the card: show the author line. Default `true` |
| `card` | object | No | With the card: your own values for it (see below) |
| `cardDurationSeconds` | number | No | With the card: seconds it stays on screen (1 to 600). Default: the whole video |
| `aspectRatio` | string | No | `ratio_9_16` (default), `ratio_16_9` or `ratio_1_1` |
| `caption` | object | No | `{ preset, alignment, disabled }`. Defaults: `wrap1`, `bottom`, `false` |
| `language` | string | No | Two-letter code of the narration's language. Detected from the prompt when omitted |
| `addStickers` | boolean | No | Add emoji stickers along the captions. Default `false` |
| `content` | object | No | `{ brandingLogo: { enabled, url, position, size } }`: your logo over the video |
| `musicId` | string | No | Background music ID from `/v1/music/list` |
| `name` | string | No | Project name (max 100 characters) |
| `webhook` | string | No | HTTPS URL called when the video is ready or the project fails |
| `metadata` | object | No | Your own data (max 5KB), sent back in the webhook |

Fields of `card`: `subreddit`, `author`, `title`, `upvotes`, `commentCount`, and the ones only some cards draw: `timeAgo` (max 40 characters), `shares` (Facebook), `subtitle` (max 80 characters, Instagram) and `verified` (Instagram). A Facebook card reads `upvotes` as reactions and an Instagram card as likes.

Gameplay clips: `minecraft-1` to `minecraft-9`, `subway-s-1` to `subway-s-11`, `temple-run-1` to `temple-run-8`, `gta-1` to `gta-12`, `fortnite-1` to `fortnite-5`, `roblox-1`, `free-fire-1` (the game is the part before the number). To use your own footage, send `selectedGame: 'custom'` and a video ID from your library as `selectedVideo`.

<Note>
  Managed teams are charged an estimate made from `targetDuration` (the narration) and the media the background needs, when the project is created; it is refunded if the project fails.

  Teams in bring-your-own-keys mode are not charged credits for generation, but need their own OpenRouter and ElevenLabs keys in Settings → AI keys, plus Gemini when the AI video model is Omni Flash. Without them the call answers `402` with `code: "missing_credentials"` and the `missingProviders` list, before anything is created or charged. To retry a create call after a timeout or 5xx, send it with an [`Idempotency-Key`](/guides/idempotency): the same key never creates or charges twice.
</Note>

## Tips for Reddit Stories

<Tip>**Give it a conflict**: a clear dilemma ("who was right?") makes the comments worth reading</Tip>
<Tip>**Set the length**: 60 to 90 seconds suits most stories; `comments_only` makes a shorter, punchier video</Tip>
<Tip>**Gameplay keeps attention**: the default background works well for long narrations</Tip>
<Tip>**Short card**: `cardDurationSeconds` hides the card after the opening so the background shows</Tip>

## Use Cases

* **Story channels**: AITA-style dilemmas, confessions and drama threads
* **Brand storytelling**: A customer story told as a social post
* **Community content**: Turn a discussion topic into a narrated thread

## Handling the Webhook Response

Webhooks are signed: check the `Hooked-Signature` header as shown in [Webhooks](/guides/webhooks#verify-the-signature).

```javascript theme={null}
// Express.js webhook handler
app.post('/webhook', (req, res) => {
  if (req.query.token !== process.env.HOOKED_WEBHOOK_TOKEN) return res.sendStatus(401);

  const { status, message, data } = req.body;
  const { videoId, projectId, url, metadata } = data;

  if (status === 'COMPLETED') {
    console.log('Story completed:', projectId, videoId);
    console.log('Download URL:', url);
    console.log('Series:', metadata?.series);
  } else if (status === 'FAILED') {
    // videoId is null when the project failed before it rendered
    console.error(videoId === null ? 'Project failed:' : 'Render failed:', message);
  }

  res.status(200).send('OK');
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.