---
title: "Derive a destination variant."
method: POST
path: "/story-projects/{projectId}/deliverables/{deliverableId}/variants"
tags: ["Projects"]
---

# Derive a destination variant.

`POST /story-projects/{projectId}/deliverables/{deliverableId}/variants`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-projects/{projectId}/deliverables/{deliverableId}/variants`
- Auth: HTTP bearer

Derive a variant of the deliverable for another destination. `destinationKey` resolves through the video destinations (404 `video_destination_not_found` / 409 `video_destination_unavailable`); one variant per (deliverable, destination) is kept — a second derive of the same key is refused, and so is the deliverable's own primary key (400 validation_failed).

## Request body

- VideoDeliverableVariantRequest: Derive a variant of the deliverable for one more destination. One variant per (deliverable, destination) is kept: a second derive of the same key is refused, and so is the deliverable's own primary key.
  - `destinationKey` string, required: A video destination key, as listed in `destinations[].key`. 404 `video_destination_not_found` when no switched-on row has it; 409 `video_destination_unavailable` when its spec fails its check.

## Response `200`

The derived variant and its resolved destination.

- VideoDeliverableVariantEnvelope
  - `variant` VideoDeliverableVariant, required: One variant of the deliverable: the deliverable's cut for one destination. The PRIMARY variant is the deliverable's own `primaryDestinationKey` row; a derived variant points at its own saved cut.
    - `id` string, required
    - `deliverableId` string, required
    - `destinationKey` string, required: The video destination key this variant is cut and checked for.
    - `trailerJobId` string, nullable: The trailer job holding this variant's cut, or null before one is cut.
    - `exportTarget` string, nullable: The export target key (`site` | `youtube` | `x`), or null.
    - `lastCheck` object, nullable: The kept check report `{ masterSha256, cutRevision, scriptVersion, specVersion, results[], checkedAt }`, or null when the variant has not been checked since the last script import (every import clears it).
    - `shareUrl` string, nullable
    - `status` 'draft' | 'cutting' | 'checking' | 'ready' | 'failed', required
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
  - `destination` VideoDestination, required
    - `key` string, required: Stable key, e.g. `chatgpt_app_review`.
    - `name` string, required
    - `family` 'plugin_review' | 'marketing' | 'social' | 'site', required
    - `description` string, required
    - `spec` object, required: What the destination asks of the video, checked on every read (a row that fails is never listed here): `content` (test cases, platforms, moments it must show, how waits are cut, which moments it draws on), `picture` (aspect, delivery, length, depth preset), `sound` (narration, music, loudness target, captions), `host` (the end system a review walkthrough is recorded in: origins, the lanes it may be recorded through, masks, controls named by role), `delivery` (link), `exports` (mp4, packet, stills) and `checks` (each a named kind with its params and `block` or `warn`), and `intent` (purpose and audience).
    - `guidanceSnippet` string, nullable, required: The Snippet whose guidance applies to this destination's videos.
    - `sourceUrls` string[], required: Where the destination's own requirements were read.
    - `verifiedAt` string, date-time, nullable, required: When the spec was last checked against `sourceUrls`.
    - `specMayBeStale` boolean, required: True when `verifiedAt` is more than 90 days old, or the row names `sourceUrls` and was never checked.
    - `specVersion` integer, required
    - `sortOrder` integer, required

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: The named project or source does not exist or is owned by another account.
- `409`: The destination's spec fails its check (`video_destination_unavailable`, `details.reason`).
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/4534f5d25540?raw)
