---
title: "Get an upload URL for the source document"
method: POST
path: "/v1/reader/audiobooks/{audiobookId}/source"
tags: ["reader"]
---

# Get an upload URL for the source document

`POST /v1/reader/audiobooks/{audiobookId}/source`

## Request

- Base URL: `https://api.ditto.audio` (the document declares no server; this is the origin it was published from)
- URL: `https://api.ditto.audio/v1/reader/audiobooks/{audiobookId}/source`
- Auth: HTTP bearer

Two steps, because the bytes go straight to storage and never through this API: `POST` here for a presigned `uploadUrl`, `PUT` your file to it, then `POST …/source/confirm`.

Send `contentHash` and the upload may be skipped entirely: if those exact bytes are already stored the response is `alreadyUploaded: true` and you go straight to confirm.

Uploading the source is what lets the **Ditto cloud renderer** narrate this audiobook — it needs the text. Device-only rendering does not.

## Path parameters

- `audiobookId` string, required

## Request body

- PresignSourceDto
  - `fileName` string, required
  - `contentType` string
  - `contentHash` string: sha-256 of the file, lowercase hex. When the bytes are already in cloud storage the response is `alreadyUploaded: true` and you skip the PUT.

## Other responses

- `403`: CLOUD_BACKUP_LIMIT — the free tier stores 10 books.

---

[API](https://skmtc.dev/ditto/apis/ditto-audio-api.md) · [All operations](https://skmtc.dev/ditto/apis/ditto-audio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ditto/apis/ditto-audio-api/revisions/30c8d7c218da?raw)
