Skip to main content
The File Upload Example posts a whole file in one request. That works until the file is bigger than what a single request can carry: a proxy, gateway, or serverless function in front of your client will usually cap a request body long before Oxen does. An upload session removes that ceiling. It’s the same protocol oxen push and oxen workspace add use for large files: you begin an upload with the file’s hash and size, and the server tells you how to split it into parts. Each part is its own request, and the server stores each one as it arrives, then checks the whole file against its hash once the last part is in. It also does something the single-request upload can’t: stage the file directly into a workspace, so it’s ready to commit, or to read back, without ever committing it.
A file may be at most 1 TiB (1,099,511,627,776 bytes). This limit is provisional, and will be raised when users need larger files.

The two steps

Both take your API key as a bearer token:
There is no separate step to finish the upload: the part that completes the file says so in its response.

The hash identifies the file

You begin an upload with the XXH3-128 hash of the file’s complete contents, written as hexadecimal. The server checks the bytes it receives against that hash before the file becomes visible, so a wrong hash gets the upload rejected, and a right one guarantees the server stored exactly what you sent.
For files large enough that you don’t want them in memory, hash incrementally: every XXH3 implementation supports feeding it the file in pieces.

Step 1: Begin the upload

Send the file’s hash and size, and, to stage the file into a workspace, a destination.

Request

Response

The response’s file field says whether to send parts.
When file is already_stored, there is nothing to send: the server has the file, and has already staged it at the destination if you gave one. The other fields are absent.

Step 2: Send the parts

Send part n as the raw bytes of the file from byte (n - 1) * part_size, part_size bytes long except for the last part. The body is not multipart, and not JSON.

Request

Output:
The server stores each part as it arrives and checks the parts against the hash in order. The part that completes the file answers with "completed": true, once the whole file matches its hash and is staged at the destination, if the upload has one.

Sending parts in parallel

Parts may arrive out of order, but only so far ahead: a part is accepted while its number is less than the first part not yet checked plus part_window. Send parts in order and keep no more than part_window of them in flight, and every part fits. A part the server cannot take yet gets 429 Too Many Requests with a Retry-After header, the seconds to wait before sending it again. That happens when a part arrives too far ahead, or while an earlier copy of the same part is still being stored.

When a response is lost

Send the part again. A part the server already stored is accepted and ignored, so a repeat is always safe. A part sent after the upload has completed gets 404 Not Found, as does any part of an upload the server aborted, which happens after a minute with no part arriving. On a 404, begin the upload again: if the file completed, begin answers already_stored, and if it was aborted, begin starts a new upload.

Errors

On Oxen.ai, beginning an upload that would take you past your storage quota gets 402 Payment Required.

Complete example

Uploads a file in parts and stages it into a workspace, sending one part at a time.
Python
Output:
From here, commit the workspace to turn the staged file into history:

When to use this

Reach for an upload session when any of these is true:
  • The file is larger than one request can carry. The most common reason. Serverless platforms in particular cap request bodies at a few megabytes.
  • You want a failure to cost one part, not the whole file. A part is sent again on its own.
  • You want parallelism. Up to part_window parts can be in flight at once.
  • You want the file staged but not committed. A destination puts it in a workspace, where you can read it back or throw it away without touching history.
For small files where none of this applies, the single-request file upload is simpler.
Older clients upload large files through POST /versions/{hash}/create, PUT /versions/{hash}/chunks, and POST /versions/{hash}/complete. Those endpoints are deprecated and will be removed in a future release, so use upload sessions for new code.