Files and uploads

Put files into a space with a presigned upload, then poll until parsing finishes.
View as Markdown

Uploading a file into a space is a two-step, presigned flow: you register the file to get a short-lived upload URL, then PUT the bytes straight to storage. Registering requires space:contribute on the space; reading requires space:read.

Uploads never stream through the gateway — you always PUT to a presigned upload URL from your own environment. This is the right path for large files and for anything you want to keep in a space (unlike the transient /process API).

The flow

1

Register the file

POST /spaces/{spaceId}/files returns a presigned upload URL.

2

Upload the bytes

PUT the raw file to that URL, with Content-Type exactly matching the mimeType you registered.

3

Poll until terminal

GET /spaces/{spaceId}/files/{fileId} until status settles, then read the download URLs.

1. Register

POST
/spaces/:spaceId/files
1curl -X POST https://api.cloudraker.com/spaces/spaceId/files \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "fileName": "project-proposal.pdf",
6 "mimeType": "application/pdf",
7 "size": 245760,
8 "processingKind": "doc-ocr",
9 "indexVectors": true,
10 "indexOntology": false
11}'

Body fields:

  • fileName (required)
  • mimeType (required) — drives both the stored record and the exact Content-Type the PUT must send.
  • size — bytes, optional.
  • processingKind — optional; same five values as /process (doc-simple, doc-ocr, doc-auto, audio-transcribe, audio-transcribe-and-diarize).
  • indexVectors / indexOntology — optional booleans, both default true (the file is indexed for search and the knowledge graph unless you opt out).

Any parentType/parentId in the body are ignored — the parent is forced to :spaceId.

Response — 201

1{
2 "id": "",
3 "uploadUrl": "https://…storage…/…",
4 "uploadExpiresAt": "2026-07-20T12:15:00.000Z",
5 "file": { }
6}

uploadUrl is a presigned upload URL valid for 15 minutes (uploadExpiresAt).

2. Upload the bytes

PUT the raw file directly to uploadUrl. This goes directly to object storage, not through the gateway. The Content-Type must be exactly the mimeType you registered — a mismatch rejects the PUT.

$curl -X PUT "<uploadUrl>" \
> -H "Content-Type: application/pdf" \
> --data-binary @./invoice.pdf

3. Poll until terminal

GET
/spaces/:spaceId/files/:fileId
1curl https://api.cloudraker.com/spaces/spaceId/files/fileId \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json"

Poll status until it settles:

StatusMeaning
waiting / processingKeep polling
storedUploaded; no processing was requested
processedProcessing succeeded
failedSee processingError

Once terminal, the record’s urls object carries minted download links (valid ~1 hour):

  • urls.file — the original file
  • urls.processedMd — extracted markdown (once produced)
  • urls.processedJson — structured / grounded JSON (once produced)

List files in a space

GET
/spaces/:spaceId/files
1curl https://api.cloudraker.com/spaces/spaceId/files \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json"

?limit defaults to 50, max 200. Files have no cursor — the list returns a single most-recent page. See Pagination and filtering.

Retry a failed file

POST
/spaces/:spaceId/files/:fileId/reprocess
1curl -X POST https://api.cloudraker.com/spaces/spaceId/files/fileId/reprocess \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{}'

Resets the file to waiting. Returns 409 if the file isn’t currently failed.

Accessing a file id from the wrong space returns 404 — existence is hidden across spaces (a space:read leak-guard). Org admins bypass space checks.

Org-level template files

Organization template files use the same presigned shape under POST /organization/templates.

Where to go next