---
title: "List sources attached to a project."
method: GET
path: "/story-projects/{projectId}/sources"
tags: ["Sources"]
---

# List sources attached to a project.

`GET /story-projects/{projectId}/sources`

## Request

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

## Query parameters

- `limit` integer
- `cursor` string

## Response `200`

Paginated source list.

- StorySourceList
  - `sources` StorySource[], required
    - `id` string, required
    - `projectId` string, required
    - `type` 'TEXT' | 'MARKDOWN' | 'URL' | 'PDF', required
    - `label` string, nullable, required
    - `uri` string, nullable, required
    - `status` 'PENDING' | 'EXTRACTING' | 'READY' | 'FAILED', required
    - `retentionPolicy` 'EXTRACTED_ONLY' | 'STORE_ORIGINAL' | 'METADATA_ONLY', required: How long the API persists source content. `EXTRACTED_ONLY` (default) stores the extracted text and metadata. `STORE_ORIGINAL` keeps the raw bytes too. `METADATA_ONLY` drops extracted text after the digest pass.
    - `byteSize` integer, nullable, required
    - `tokenEstimate` integer, nullable, required
    - `contentHash` string, nullable, required
    - `extractedTextPreview` string, nullable, required
    - `extractedObjectKey` string, nullable, required
    - `rawObjectKey` string, nullable, required
    - `failureCode` string, nullable, required
    - `failureMessage` string, nullable, required
    - `producer` string, nullable, required: Who made this item (`hackernewsradio`, or `integration:<integrationId>` for an item kept from a connected product); null for material a person attached. Producer items are episode material, not canon: Bible generation never reads them, and a season pins one only when it was curated into that season.
    - `externalId` string, nullable, required: The producer's id for this item. Null once the source is deleted or FAILED, which frees the item to be sent again.
    - `metadata` object, nullable, required
    - `originatedAt` string, date-time, nullable, required: When the source material itself was made (`metadata.originatedAt`), e.g. a thread's posting time; null when the producer did not say. A project with `seasonCadence: "monthly"` files the episode made from this source under that month, falling back to `createdAt`.
    - `digest` StorySourceDigest, required: Structured digest of a single source. Always source-grounded; the model is instructed not to invent claims that the source does not make.
      - `summary` string, required: 3-6 sentence plain-prose summary.
      - `thesis` string, required: Single sentence capturing the source's dominant point.
      - `claims` object[], required
        - `statement` string, required
        - `kind` 'fact' | 'estimate' | 'opinion' | 'forecast' | 'quotation' | 'definition', required
        - `attribution` string, nullable, required
      - `evidence` object[], required
        - `claimIndex` integer, required
        - `support` string, required
      - `gaps` string[], required
      - `rightsNotes` string[], required
    - `digestStatus` 'NOT_STARTED' | 'PENDING' | 'RUNNING' | 'READY' | 'FAILED' | 'SKIPPED', required: Lifecycle of the post-extraction LLM digest. `PENDING` with digestFailureCode `provider_capacity_backoff` means Sleeper Hit scheduled a delayed retry. `SKIPPED` is terminal (stop polling) and is not a fault; the underlying source remains usable. It means either the digest could not run (sidecar/provider missing or no extracted text), or, for a full-context producer item (`producer` set and `metadata.sourceContextMode: "full"`), that it is skipped by design (`digestFailureCode: digest_skipped`, `digestFailureMessage: "Used in full by its episode plan."`) because its plan reads every character.
    - `digestFailureCode` string, nullable, required
    - `digestFailureMessage` string, nullable, required
    - `digestedAt` string, date-time, nullable, required
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
    - `extractedAt` string, date-time, nullable, required
  - `nextCursor` string, nullable, required

## Other responses

- `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.
- `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)
