Upload sessions
A document over 100 MB cannot go in one request. Open a session, send it in parts, and end the session — which assembles the object and starts the same ingest a direct upload would have.
When you need one
Only above 100 MB. Under that, POST /files is one request and does the same thing. Note that the organisation's tier caps upload size independently — 300 MB at the top rung — so a session is for the band between the single-request ceiling and the tier's, not for arbitrarily large files.
The whole flow
# 1. open a session — you get back a key
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/start \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "fileName=acme-vdr-bundle" \
-F "fileType=application/pdf" \
-F "fileExt=pdf"
# → { "success": true, "data": { "key": "tmp_H4nQ8bV2…" } }
# 2. send each chunk, 1-based and in any order
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/append \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "key=tmp_H4nQ8bV2…" -F "partNumber=1" -F "file=@part-001.bin"
# → { "success": true, "data": { "partNumber": 1, "etag": "\"a3f…\"" } }
# 3. hand back every etag you collected, as a JSON *string*
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/end \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "key=tmp_H4nQ8bV2…" \
-F 'parts=[{"partNumber":1,"etag":"\"a3f…\""},{"partNumber":2,"etag":"\"b71…\""}]'
# → the ingest jobFour things that catch people
- Every endpoint is
multipart/form-data, includingendandabort, which carry no file. There is no JSON form. partsis a JSON string inside a form field — an array of{ partNumber, etag }, stringified. Send the etags exactly as returned, quotes included.- Part numbers are 1-based and each part is itself capped at 100 MB. Parts may be sent concurrently and out of order. The ceiling on the number is the organisation's upload limit divided by a 5 MB minimum part size, doubled — 4 parts at Starter's 10 MB, 120 at Gold's 300 MB — so a part number above it is refused with a
400telling you to use bigger chunks. Use parts of at least 5 MB and this never bites. appendhas its own rate limit — 300/min rather than the 15/min every other write shares. A single upload is onestart, many parts and oneend, and on the shared budget a large file exhausted it partway through.
The assembled object is checked against the tier's upload ceiling at end, not at start — because that is the first moment its real size exists. A session that opens successfully can still be refused when you close it.
Start a session
POST/v2/workspaces/:workspaceId/files/upload_session/start
Requires Read & write on the workspace
Opens a multipart upload and returns the key every subsequent call needs. The key is scoped to this workspace — a session cannot be appended to or completed from another one.
There is no jobType: v2 ingests one way, extract then vectorize. If you are porting from v1, drop that parameter — it would be silently ignored.
Parameters
fileName | string · required1–64 characters. Without the extension. |
|---|---|
fileType | string · requiredMIME type, 1–64 characters, e.g. application/pdf. |
fileExt | string · required1–10 characters, e.g. pdf. This is what decides how the document is read. |
expiresInDays | numberStamps a deletion date on the resulting file. Nothing sets one by default. |
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/start \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "fileName=acme-vdr-bundle" \
-F "fileType=application/pdf" \
-F "fileExt=pdf"{
"success": true,
"data": {
"key": "tmp_H4nQ8bV2xJ6mR9pKtZ3wLc"
}
}Append a part
POST/v2/workspaces/:workspaceId/files/upload_session/append
Requires Read & write on the workspace
Uploads one chunk. Keep the etag — end needs every one of them, and there is no way to ask for them back.
An unknown, expired or already-finished session answers 400, not 404.
Parameters
key | string · requiredThe session key from start. |
|---|---|
partNumber | number · requiredInteger, from 1. Bounded by the organisation's upload limit at a 5 MB minimum part size (4 parts at Starter, 120 at Gold), and never above 10,000. Parts may be sent out of order and concurrently. |
file | file · requiredThe chunk. Non-empty, up to 100 MB. |
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/append \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "key=tmp_H4nQ8bV2xJ6mR9pKtZ3wLc" \
-F "partNumber=1" \
-F "file=@part-001.bin"{
"success": true,
"data": {
"partNumber": 1,
"etag": "\"a3f19c47b8e02d5619f4c0a7e2b8d331\""
}
}End a session
POST/v2/workspaces/:workspaceId/files/upload_session/end
Requires Read & write on the workspace
Assembles the parts into one object and enqueues the ingest — the same job a direct upload returns. This is where the file's real size is known, so it is where the tier's upload ceiling is enforced.
Spends credits — 1 per page, once the document is read.
Parameters
key | string · requiredThe session key. |
|---|---|
parts | string · requiredA JSON string: [{ "partNumber": 1, "etag": "…" }, …], one entry per part you appended, etags verbatim. |
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/end \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "key=tmp_H4nQ8bV2xJ6mR9pKtZ3wLc" \
-F 'parts=[{"partNumber":1,"etag":"\"a3f…\""},{"partNumber":2,"etag":"\"b71…\""}]'{
"success": true,
"data": {
"id": "dsj_L8vB2mQ6xR3kW9pZ",
"fileId": "ds_M3xJ8pQ1vK5nB7wT",
"type": "extract",
"status": "NOT_STARTED",
"statusText": null,
"startedAt": null,
"finishedAt": null,
"canceledAt": null,
"createdAt": 1756108800000,
"updatedAt": 1756108800000
}
}Abort a session
POST/v2/workspaces/:workspaceId/files/upload_session/abort
Requires Read & write on the workspace
Discards the session and the parts uploaded so far. Nothing is ingested and nothing is charged — credits are spent when a document is read, and an aborted upload never becomes a document.
Worth calling on a failed upload rather than abandoning the session: it releases the partial object immediately.
Parameters
key | string · requiredThe session key. |
|---|
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/files/upload_session/abort \
-H "x-api-key: $RAGEXTRACT_API_KEY" \
-F "key=tmp_H4nQ8bV2xJ6mR9pKtZ3wLc"{
"success": true,
"total": 0
}