---
title: "Create a new story project workspace."
method: POST
path: "/story-projects"
tags: ["Projects"]
---

# Create a new story project workspace.

`POST /story-projects`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-projects`
- Auth: HTTP bearer

Creates an empty project, stamped with the workspace role it is made under (`workspaceRole`; omitted, your current role). 403 `product_kind_not_for_role` for `kind: product` from a Screenwriter account: product projects belong to Content creator and Business workspaces. Requires `Idempotency-Key` for safe retries.

## Headers

- `Idempotency-Key` string, required

## Request body

- StoryProjectCreate
  - `name` string, required
  - `description` string
  - `metadata` object: Your own JSON for cross-reference, with keys the platform reads: `coverageCalibrations` (up to 5 names, e.g. `["comedy"]`) opts this project's screenplay coverage into the series coverage calibrations an admin has written (a name with no active calibration is ignored); `seasonCadence` (`"monthly"`) files new episodes by month; `seasons` is the project's season list. A malformed value is refused.
  - `kind` 'story' | 'product': What this project is. Omitted: the account's role default, `product` for a Business account and `story` otherwise (the ChatGPT app always starts a `story`). A Screenwriter account (workspace role `writer`) makes story projects only: `product` is refused with 403 `product_kind_not_for_role`, and omitted is always `story` for it. A `product` project's Bible carries the product block (alternatives, positioning, call to action, do-not-claim, compliance) and is judged by the `product_bible` rubric.
  - `contentGuard` boolean: Keep everything written for the project suitable for ages 13 and up. Default false.
  - `medium` 'audio' | 'video': What the story is made as: `audio` (a podcast or audio drama) saves the Series Bible's `productionMedium: audio`; `video` (for the screen) leaves the screen medium for the Bible to settle. Omitted: undecided. A product project takes neither.
  - `recurring` boolean: True: a recurring show, making new episodes on a schedule from its sources. Its show is made with the project (the read's `showSeriesId`), not set up and off: nothing runs or spends until its setup and caps are agreed, a preview is heard and it is turned on (`/publishing-series/{seriesId}/show`). Implies `medium: audio`. A recurring show is audio for now: make it a podcast (`medium: audio`) to make it recurring.
  - `workspaceRole` 'writer' | 'creator' | 'business': The workspace role to stamp the project with. Omitted: your current workspace role. With no `kind`, it also picks the kind (`product` for `business`, `story` otherwise). Every project is stamped with the workspace role it was made under (`writer`, `creator` or `business`): the creator's current role, or the role the create names. The sidebar's project list shows the current role's projects first, with one click to all of them. Null for a project made before roles were stamped, which shows under every role.

## Response `200`

Created project envelope.

- StoryProjectEnvelope
  - `project` StoryProject, required
    - `id` string, required
    - `name` string, required
    - `description` string, nullable, required
    - `metadata` object, nullable, required
    - `kind` 'story' | 'product', required: What this project is: `story` (characters, seasons, episodes; judged on premise and series engine) or `product` (personas, features, truth; judged on the product Bible). It selects the coverage rubric and the Bible's shape, and once the project exists it is the project's own: it never follows the account. A create with no `kind` takes the account's role default: `product` for a Business account, `story` otherwise; the ChatGPT app always starts a `story`.
    - `workspaceRole` 'writer' | 'creator' | 'business', nullable, required: Every project is stamped with the workspace role it was made under (`writer`, `creator` or `business`): the creator's current role, or the role the create names. The sidebar's project list shows the current role's projects first, with one click to all of them. Null for a project made before roles were stamped, which shows under every role.
    - `seasonCadence` 'monthly', nullable, required: How a new episode is filed into a season. `monthly`: the season named for the month (UTC) its source material was made (`originatedAt` on the source, else when the source was added, else when the episode was made), created after the last season when the month is new. Null: numbered seasons (the season the plan, its season run or its Series Bible entry names, else Season 1). Stored as `metadata.seasonCadence`.
    - `medium` 'audio' | 'video', nullable, required: What the story is made as: `audio` (a podcast or audio drama: voices, music and sound, no picture) or `video` (for the screen). Read from the Series Bible's `productionMedium` (`audio` is audio, any visual medium is video); null until the Bible says.
    - `recurring` boolean, required: A recurring show: new episodes on a schedule from its sources. True when the project's show is made by Sleeper or by an outside producer under a standing approval.
    - `showSeriesId` string, nullable: The show's publishing series: the id `GET /publishing-series/{seriesId}/show` (MCP get_show, `sleeperhit show get`) takes. Null when the project is not a show.
    - `contentGuard` boolean, required: Keeps every script, plan and Series Bible written for the project suitable for ages 13 and up: no strong profanity, no graphic violence, no sexual content. On for a project started from ChatGPT; off otherwise unless set.
    - `brandLogoUrl` string, nullable, required: The project's brand logo on the studio's media host, or null. A project's brand logo is its own mark: a PNG, JPEG or WebP image of up to 2 MB and 4096 pixels on a side (a transparent PNG on a dark ground shows best), sent as `brandLogo` on the project update — `{ base64, contentType? }` with the file's bytes, or `{ sourceUrl }`, a public https address it is fetched from once. The server decodes it and stores its own clean copy on the studio's own media host (a PNG stays a PNG; a JPEG becomes a WebP), so a file that does not decode as the image it says it is is refused; `brandLogo: null` removes it. The project read returns its address as `brandLogoUrl`. A deck's or a trailer's closing sequence can end on it (`logoCard: true`): the closing's save copies the project's logo at that moment, so after replacing the logo, save the closing again to show the new one. Free.
    - `clickSound` object, nullable, required: The project's own click sound, or null for the stock click. A project's click sound is the product's own interface click, played on every press in every screen beat of the project's trailers instead of the studio's stock click: a sound from My Library (`librarySoundId`, that sound's id — import the product's own file there first), with `trimMs` (0–2000) skipping silence at its start and `gainDb` (-24 to +12) levelling it against the stock click once; each click plays at most 400 ms of it. Sent as `clickSound` on the project update, or null to go back to the stock click. A sound that is gone, or too short once trimmed, plays the stock click and the beat says why. Free; the next render of each trailer plays it.
      - `librarySoundId` string, required: The My Library sound it plays.
      - `label` string, required: The sound's name in My Library when it was picked.
      - `url` string, required: The sound's file on the studio's media host.
      - `trimMs` number, required: Milliseconds skipped at its start.
      - `gainDb` number, required: dB over each click's own level.
      - `durationMs` number, nullable, required: Its length, when My Library knows it.
    - `archivedAt` string, date-time, nullable, required
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
    - `sourceCount` integer
    - `yourRole` 'owner' | 'editor' | 'viewer': The caller's place on the project: `owner`, or `editor` / `viewer` on a product project shared with them (GET /story-projects/{projectId}/members). An editor changes the project's content but cannot delete it, archive it or manage its members; a viewer reads.
    - `show` ProjectListShow
      - `seriesId` string, required: The show's publishing series (`showSeriesId`).
      - `state` 'not_set_up' | 'off' | 'on' | 'paused' | 'external', required: The show's state: `on`, `off`, `paused`, `not_set_up`, or `external` (an outside app makes it).
      - `timezone` string, required: The show's clock (IANA), for its days and times.
      - `publishedEpisodes` integer, required: Episodes published in the show's feed (episodes only, never trailers or extras).
      - `nextRunAt` string, date-time, nullable, required: When the next scheduled run makes episodes. Null when the show is not on or nothing is scheduled.
      - `next` string, nullable, required: `nextRunAt` in words, in the show's clock: "tonight 7 pm Central".
      - `latestEpisode` object, nullable, required: The newest published episode, or null before the first.
        - `releaseId` string, required: The feed release (GET /publishing-releases/{releaseId}).
        - `scriptId` string, nullable, required: The script the episode was made from, when the release names one.
        - `href` string, nullable, required: The episode's page in the app (a path on the studio's site), when it has a script.
        - `code` string, nullable, required: Its number: "S4E429" in a season, "Ep 12" without one.
        - `title` string, required
        - `durationMs` integer, nullable, required: Its audio length, when known.
        - `publishedAt` string, date-time, 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.
- `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/64c1102ccb2f?raw)
