Files

Register a document once, by URL or presigned upload. Then reuse it across runs.
View as Markdown

/v1/files is the reusable document corpus behind the capability endpoints. Register a file once. Every later run then takes {"file": {"id": "…"}}. The platform does not fetch the document again and does not parse it again.

Files you register vs files a run creates

Both kinds are files, and an id addresses both. But they follow different rules:

Registered with POST /v1/filesCreated inline by a run
Created byYou{"file": {"url": …}} on a verb, or a run’s output
LifetimePersistent — until you DELETE itExpires with the run’s ttl (default 24 hours, max 7 days)
ReuseAcross any number of runsOnly while the run that created it lives
Best forDocuments you will run several capabilities overOne-shot processing

A run that creates a file inline still returns its id. The parse is reusable inside that run’s TTL. If you plan to keep working with the document, register it first. Use keep to extend the lifetime of a run’s files after the fact.

Register by URL

This is the production-default path. The platform fetches the bytes server-side over http(s).

$curl -X POST https://api.cloudraker.com/v1/files \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
> "name": "w9.pdf",
> "processing": "auto"
> }'
1{
2 "object": "file",
3 "id": "a04d6597-4e34-4a99-94ea-964c289a4c68",
4 "name": "w9.pdf",
5 "mimeType": "application/pdf",
6 "status": "uploading",
7 "createdAt": "2026-07-25T18:00:39.453Z"
8}

With a url, the platform sniffs the MIME type from the response. It drops any mimeType you send with the url. Send url or name + mimeType, not both.

Sources must serve a Content-Length. The fetch streams directly into storage. An origin that answers with chunked transfer encoding, or with compressed content and no usable length, fails with 502 file_upload_failed. Static file hosts work. HTML pages and gzip-encoded endpoints often do not. Upload those with a presigned PUT instead.

Presigned upload

Use this for local files, large files, and files not reachable by URL. No multipart. Pure JSON.

1

Reserve the record

$curl -X POST https://api.cloudraker.com/v1/files \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "name": "recording.mp3", "mimeType": "audio/mpeg", "processing": "transcribe_diarize" }'

The response carries uploadUrl and uploadExpiresAt. The URL is valid for 15 minutes.

2

PUT the bytes

$curl -X PUT "<uploadUrl>" \
> -H "Content-Type: audio/mpeg" \
> --data-binary @./recording.mp3

The Content-Type header must equal the mimeType you registered exactly. Storage signs it into the URL and rejects a mismatch. This is the most common upload failure.

3

Poll until ready

GET /v1/files/:id until status is ready. Then read urls.

Read a file

$curl https://api.cloudraker.com/v1/files/a04d6597-4e34-4a99-94ea-964c289a4c68 \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY"
1{
2 "object": "file",
3 "id": "a04d6597-4e34-4a99-94ea-964c289a4c68",
4 "name": "w9.pdf",
5 "mimeType": "application/pdf",
6 "status": "ready",
7 "createdAt": "2026-07-25T18:00:39.453Z",
8 "urls": {
9 "content": "https://cdn.cloudraker.com/…/latest?token=…&fn=w9.pdf",
10 "markdown": "https://cdn.cloudraker.com/…/processed.md?token=…&fn=w9.pdf",
11 "json": "https://cdn.cloudraker.com/…/processed.json?token=…&fn=w9.pdf"
12 }
13}
statusMeaning
uploadingRegistered. The bytes have not landed yet.
processingBytes stored. The platform reads them.
readyDone. urls is present.
failedSee error.

urls.content is the original bytes. urls.markdown and urls.json are the parse byproducts. They appear only after the document was parsed. All three URLs are signed and expire after about 1 hour. Fetch the content. Do not store the URL.

Audio transcripts

Audio and video produce no markdown byproduct. urls.markdown stays absent for them. The transcript is urls.json.

Fetch that URL to read the transcript. The body is one object with a detected language and an ordered segments array.

1{
2 "language": "en",
3 "segments": [
4 {
5 "start": 0.53,
6 "end": 4.12,
7 "text": "Good morning, thanks for calling the clinic.",
8 "speaker": "SPEAKER_00",
9 "words": [
10 { "word": "Good", "start": 0.53, "end": 0.71, "score": 0.94, "speaker": "SPEAKER_00" },
11 { "word": "morning,", "start": 0.78, "end": 1.14, "score": 0.91, "speaker": "SPEAKER_00" }
12 ]
13 },
14 {
15 "start": 4.60,
16 "end": 7.02,
17 "text": "Hi, I would like to book a follow-up.",
18 "speaker": "SPEAKER_01",
19 "words": [
20 { "word": "Hi,", "start": 4.60, "end": 4.79, "score": 0.88, "speaker": "SPEAKER_01" }
21 ]
22 }
23 ]
24}
FieldWhat it is
languageThe detected language code, such as en.
segments[]The transcript in playback order. One entry per continuous run of speech.
segments[].startSeconds from the start of the recording. Seek here to play the segment.
segments[].endSeconds at which the segment stops.
segments[].textWhat the speaker said in that segment.
segments[].speakerThe speaker label, such as SPEAKER_00. Present only with transcribe_diarize.
segments[].words[]Word-level timings. Each word carries word, start, end, and an alignment score. Diarization also stamps speaker on each word.

Join every segments[].text in order to rebuild the transcript as plain text.

transcribe and transcribe_diarize both return word-level timings. Only transcribe_diarize adds speaker. Choose transcribe when speaker labels do not matter.

Speaker labels are positional, not identities. SPEAKER_00 is the first voice the platform separated. The same person can get a different label in another recording.

A word the aligner cannot place comes back without start and end. This is rare. Fall back to the segment timing for those words.

Where the transcript arrives, per surface

The transcript is the same object everywhere. Only the delivery differs.

SurfaceHow you get the transcript
GET /v1/files/:idFetch urls.json yourself.
GET /process/:id with include=results,content&format=jsonThe API inlines it in files[].content. No second fetch.
POST /v1/parseFetch output.jsonUrl.
POST /v1/extract and the other capability verbsNot inlined. Read the file, then fetch its urls.json.

On /process, format=markdown returns null for audio content. Audio has no markdown byproduct. Always ask for format=json on a recording.

List and delete

$curl "https://api.cloudraker.com/v1/files?limit=50" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY"
$
$curl -X DELETE https://api.cloudraker.com/v1/files/a04d6597-4e34-4a99-94ea-964c289a4c68 \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY"

?limit caps the page (1–200, default 50), newest first. There is no cursor today. Paging is by limit only. DELETE returns 204 and removes the parse byproducts with the file. Runs that already used the file keep their results.

Processing

processing selects how the platform reads the document. It applies when you register the file, and again in file.processing on any capability call.

ValueUse for
autoDefault for documents. Selects per document and upgrades to OCR on scans.
simpleBorn-digital PDFs with a real text layer. Fastest.
ocrScans and photographed pages.
transcribeAudio where speaker separation does not matter.
transcribe_diarizeDefault for audio and video. Separates speakers.

Parsing is automatic. A capability parses what is not parsed and never re-parses what is. A parse call first is optional, not a prerequisite.

Reuse a file

$curl -X POST https://api.cloudraker.com/v1/extract \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "file": { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68" },
> "schema": { "type": "object", "properties": { "business_name": { "type": ["string", "null"] } } }
> }'

Parse once, run many. Several narrow extractions, a redaction, and a fill can all read the same file id. None re-reads the document. Send up to 100 file refs in one run with files: [...].

Next steps