Files
Upload batch input files as JSONL (a direct multipart upload for smaller files, a presigned URL for large ones), then read file metadata and content.
Batch input and result files are JSONL blobs held by the gateway. Input files
carry purpose: "batch"; result files are produced by the batch itself, and a
batch create that rejects records writes a rejections file. Files are limited
to 200 MB and 50,000 records, and are retained for 30 days.
There are two ways to upload an input file:
- Direct multipart (
POST /v1/files): one request, best for files you send from a server. - Presign + complete (
POST /v1/files/presignthenPOST /v1/files/{id}/complete): upload the bytes straight to storage with a signed URL, then register the file. Use this for large files or browser uploads.
The file object returned by every endpoint below is:
| Field | Type | Description |
|---|---|---|
id | string | The file id (file_…). Pass it as input_file_id when creating a batch. |
object | string | Always "file". |
bytes | integer | File size in bytes. |
created_at | integer | Unix seconds when the file was registered. |
expires_at | integer | Unix seconds when the file is deleted (30 days after creation). |
filename | string | A server-assigned name. |
purpose | string | "batch" for input files, "batch_output" and "batch_error" for a batch's result files, and "batch_rejections" for the records a create rejected. |
Upload a file (direct)
POST https://inference.openrelay.inc/v1/filesA multipart/form-data body with two parts: purpose (must be batch) and
file (the JSONL).
curl https://inference.openrelay.inc/v1/files \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-F "purpose=batch" \
-F "file=@requests.jsonl"{
"id": "file-a1b2c3d4e5f6a7b8c9d0e1f2",
"object": "file",
"bytes": 412,
"created_at": 1752460800,
"expires_at": 1755052800,
"filename": "file-a1b2c3d4e5f6a7b8c9d0e1f2.jsonl",
"purpose": "batch"
}A file over 200 MB returns 413; a file with more than 50,000 records (blank
lines are not records), a missing or empty file part, or a purpose other
than batch returns 400. If image inputs push you over the size limit,
compress them first; see
Best practices.
The upload checks only that the file is JSONL. A file that is not returns 400
with code: "invalid_file" and a message saying why: it contains a NUL byte
(a binary file, or text saved as UTF-16 or UTF-32), it starts with a UTF-16 or
UTF-32 byte-order mark (save it as UTF-8), it has no records, or none of its
lines is valid JSON (lines over 64 KiB are not checked here). The records
themselves are checked when you create a batch from the file, which
rejects the invalid ones and
runs the rest.
CLI: orl files upload requests.jsonl streams the same multipart upload.
An upload takes no Idempotency-Key: repeating one stores a second file with a
new id, which costs nothing until a batch runs it. The batch create does take
one; see Retry a create safely.
Upload a file (presigned)
For large files, or when uploading from a browser, get a signed URL, PUT the
bytes to it, then register the file.
1. Presign
POST https://inference.openrelay.inc/v1/files/presign| Field | Type | Description |
|---|---|---|
bytes | integer (required) | The exact size of the file you will upload, 1 to 200 MB. |
filename | string | Optional; ignored by the server (parity with direct upload). |
curl https://inference.openrelay.inc/v1/files/presign \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "bytes": 5242880 }'{
"file_id": "file-a1b2c3d4e5f6a7b8c9d0e1f2",
"put_url": "https://…signed-url…",
"expires_at": 1752461701
}The put_url is valid for 15 minutes. No file is registered yet.
2. PUT the bytes
Upload the raw JSONL to put_url with an HTTP PUT:
curl -X PUT "$PUT_URL" --upload-file requests.jsonl3. Complete
POST https://inference.openrelay.inc/v1/files/{id}/completeRegisters the uploaded object. The gateway checks it as the direct upload checks a file (the size, the record count, and that it is JSONL) and returns the file object. This call is idempotent: completing an already-registered id returns its existing file object.
curl https://inference.openrelay.inc/v1/files/file-a1b2c3d4e5f6a7b8c9d0e1f2/complete \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-X POST{
"id": "file-a1b2c3d4e5f6a7b8c9d0e1f2",
"object": "file",
"bytes": 5242880,
"created_at": 1752460900,
"expires_at": 1755052900,
"filename": "file-a1b2c3d4e5f6a7b8c9d0e1f2.jsonl",
"purpose": "batch"
}Complete returns 404 if nothing was uploaded to the id, and 400 if the
object is empty, over 200 MB, over 50,000 records, or not JSONL
(invalid_file); the object is discarded in those cases. It returns 429
when too many uploads are being processed at once: retry shortly.
Get file metadata
GET https://inference.openrelay.inc/v1/files/{id}curl https://inference.openrelay.inc/v1/files/file-a1b2c3d4e5f6a7b8c9d0e1f2 \
-H "Authorization: Bearer $OPENRELAY_API_KEY"Returns the file object, or 404 if the id is unknown to your organization.
Download file content
GET https://inference.openrelay.inc/v1/files/{id}/contentStreams the raw file bytes (application/octet-stream). Use it to read a
batch's output_file_id or error_file_id, a create's
rejected_records_file_id, or to read back an input file.
curl https://inference.openrelay.inc/v1/files/file-batch_9f8e7d6c5b4a-output/content \
-H "Authorization: Bearer $OPENRELAY_API_KEY" \
-o results.jsonl