Skip to main content

Sources

A source is a transcribed file uploaded into a project — typically a voice memo, podcast snippet, or article import. Sources feed the AI pipeline that produces notes and post drafts; they’re not directly visible to end users in the published feed. The v1 API ships a single-resource read endpoint for sources. There is no list endpoint in v1; if you need to enumerate sources, traverse notes (GET /v1/notes) and follow each note’s source attachment when it ships in v1.x.

GET /v1/sources/{sourceId}

Read a single source row. The transcription body is truncated to the first 2000 characters for safety — long-form transcripts are excluded from the API surface, intentionally:
  • Sources can hold customer audio they uploaded with no expectation it would leave the dashboard. Exposing the full body would be a surprise.
  • Some sources are large (multi-megabyte transcripts of hour-long calls). Pulling them through a JSON response would amplify rate-limit usage without any real client benefit.
If you need the full transcript for a programmatic workflow, file a feature request and we’ll consider a streaming download endpoint in Phase 4.

Response

Field reference

The S3 storage keys, internal upload identifiers, the original processing-flusher state, and the secret presigned URLs are intentionally not in this response. We never return them through the public API.

Truncation contract

  • textPreviewTruncated === true → the preview is exactly 2000 characters and there is more transcript on the server.
  • textPreviewTruncated === false AND hasFullText === true → the preview is the full transcript.
  • hasFullText === false → the source has no transcript yet (still processing or processing failed). textPreview will be empty.
We do not truncate at character boundaries that could split UTF-8 codepoints in the middle of a multi-byte sequence — the implementation slices safely at the codepoint level. Your client can render the preview without sanitisation.

Errors


POST /v1/sources

Create a source. Required scope: sources:write. Two input shapes:
  • type: "text" — synchronous. The caller supplies the prepared transcript and the row is stored with status: Success immediately. Returns the new Source envelope (200).
  • type: "file" — asynchronous. The caller first calls POST /v1/uploads to obtain a presigned S3 PUT URL + opaque uploadId handle, PUTs the bytes, then references the handle here. Returns a Job envelope (200) with type: SOURCE_INGEST_FILE; poll GET /v1/jobs/{jobId} until status: COMPLETED and the resulting result.sourceId becomes available.

Request body — type: "text"

Request body — type: "file"

Response — type: "text"

Response — type: "file"

Once the worker finishes:
GET /v1/sources/{sourceId} then returns the fully-processed source. The async path also enforces the per-plan AI spend cap; calls that would push the daily cost over the cap return 402 spend_cap_exceeded without enqueueing a job. See docs/api/v1/jobs.md for the full lifecycle and pricing.

Errors


What’s NOT here (yet)

  • List endpoint. No GET /v1/sources. Plan-side decision; if you have a use case, file a feature request.
  • Full transcript download. See above.
  • File / audio download. Storage URLs are private. The dashboard’s download links are signed and time-limited; we won’t expose them via the API in v1.
  • Update / delete endpoints. PATCH / DELETE are out of scope for v1.