File Upload API
The File Upload API is the entry point for supplying supporting files (PowerPoint, PDF, images, etc.) to downstream jobs such as AutoGenerator and Template Converter.
Two endpoints cover the upload patterns:
POST /api/v1/preprocess— chunked upload.POST /api/v1/validate— validate previously uploaded files and/or web links.
Full schemas and try-it-out forms live in the interactive API Reference.
Authentication
All endpoints require a Bearer token:
Authorization: Bearer YOUR_API_KEY
See Getting Started → Authentication.
Response envelope
Every endpoint returns the canonical envelope: { "success": true, "data": { ... } }
on success or the standard
error envelope on error.
Endpoint summary
| Method | Path | Description | Sync/Async |
|---|---|---|---|
POST | /api/v1/preprocess | Preprocess a file in chunks | Sync |
POST | /api/v1/validate | Validate uploaded files and/or web links | Sync |
All endpoints are in the auto_generator rate-limit category. See
Developer Guide → Usage quotas and rate limits.
POST /api/v1/preprocess — Chunked upload
Accepts one chunk of a multi-chunk file upload. The caller drives
chunking via chunkIndex and totalChunks; chunks for the same upload
share a requestIdentifier. Once all chunks for a requestIdentifier
arrive, the file is assembled and made available to downstream
operations.
A parallel chunked-upload endpoint exists for Template Converter
specifically (POST /api/v1/template-converter/preprocess) and uses
the same request shape; only .pptx files are accepted there.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
fileContent | string | yes | Chunk content as base64 / data: URL. |
fileName | string | yes | Original filename including extension. |
chunkIndex | integer ≥ 0 | yes | Zero-based index of this chunk. |
totalChunks | integer ≥ 1 | yes | Total number of chunks for this upload. |
requestIdentifier | string | yes | Caller-supplied identifier that groups chunks of the same upload. |
Example (one chunk)
- cURL
- Node.js
- Python
curl -X POST https://api.prezent.ai/api/v1/preprocess \
-H "Authorization: Bearer $PREZENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fileName": "big-deck.pptx",
"fileContent": "UEsDBBQAAAAIAA...",
"chunkIndex": 0,
"totalChunks": 4,
"requestIdentifier": "upload-2026-05-18-001"
}'
const axios = require('axios');
const preprocessChunk = async () => {
const { data } = await axios.post(
'https://api.prezent.ai/api/v1/preprocess',
{
fileName: 'big-deck.pptx',
fileContent: 'UEsDBBQAAAAIAA...',
chunkIndex: 0,
totalChunks: 4,
requestIdentifier: 'upload-2026-05-18-001'
},
{
headers: {
Authorization: `Bearer ${process.env.PREZENT_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
console.log(data);
return data;
};
preprocessChunk();
import os
import requests
url = "https://api.prezent.ai/api/v1/preprocess"
headers = {
"Authorization": f"Bearer {os.environ['PREZENT_API_KEY']}",
"Content-Type": "application/json",
}
payload = {
"fileName": "big-deck.pptx",
"fileContent": "UEsDBBQAAAAIAA...",
"chunkIndex": 0,
"totalChunks": 4,
"requestIdentifier": "upload-2026-05-18-001",
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
print(response.json())
Success response (200)
{
"success": true,
"data": {
"request_identifier": "upload-2026-05-18-001",
"file_name": "big-deck.pptx",
"message": "Chunk received."
}
}
POST /api/v1/validate — Validate files / links
Validates a set of previously uploaded files (referenced by
fileIdentifiers) and/or a list of web links. At least one of
fileIdentifiers or webLinks must be non-empty.
Call this after every chunk of a file has been sent to
POST /api/v1/preprocess. The
file_id it returns is the id to pass in the files array of
AutoGenerator and Posters
requests.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
fileIdentifiers | object | conditional | Map of requestIdentifier → fileName, one entry per file. Use the same requestIdentifier you sent with that file's /preprocess chunks. |
webLinks | array of URI | conditional | List of URLs to validate. |
Example
- cURL
- Node.js
- Python
curl -X POST https://api.prezent.ai/api/v1/validate \
-H "Authorization: Bearer $PREZENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fileIdentifiers": { "3f9c2b1e-7a4d-4e8b-9c1f-2d6a5b8e0f71": "context.pdf" },
"webLinks": ["https://example.com/whitepaper"]
}'
const axios = require('axios');
const validateFiles = async () => {
const { data } = await axios.post(
'https://api.prezent.ai/api/v1/validate',
{
fileIdentifiers: { '3f9c2b1e-7a4d-4e8b-9c1f-2d6a5b8e0f71': 'context.pdf' },
webLinks: ['https://example.com/whitepaper']
},
{
headers: {
Authorization: `Bearer ${process.env.PREZENT_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
console.log(data);
return data;
};
validateFiles();
import os
import requests
url = "https://api.prezent.ai/api/v1/validate"
headers = {
"Authorization": f"Bearer {os.environ['PREZENT_API_KEY']}",
"Content-Type": "application/json",
}
payload = {
"fileIdentifiers": {"3f9c2b1e-7a4d-4e8b-9c1f-2d6a5b8e0f71": "context.pdf"},
"webLinks": ["https://example.com/whitepaper"],
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
print(response.json())
Success response (200)
{
"success": true,
"data": {
"files": [
{
"file_id": "cbef85d9-d8ef-43c4-aa89-317f7d255bab",
"unique_id": "cbef85d9-d8ef-43c4-aa89-317f7d255bab",
"status": "success",
"file_type": "application/pdf",
"file_size": 1.77,
"number_of_pages": 3,
"is_corrupted": false,
"is_password_protected": false,
"log": "success"
}
],
"web_links": [
{ "url": "https://example.com/whitepaper", "valid": true }
],
"message": "Validation complete."
}
}
| Field | Description |
|---|---|
file_id | Id of the validated file. Pass this in the files array of generation requests. |
unique_id | Same value as file_id. |
status | "success" when the file can be used. Any other value means it failed validation. |
file_type | MIME type of the file. |
file_size | File size in MB. |
number_of_pages | Page or slide count. |
is_corrupted | true if the file could not be read. |
is_password_protected | true if the file is password protected. Remove the password and upload again. |
log | Processing message. |
A failed file is not reported with an HTTP error. Its entry has a
status other than "success", and is_corrupted or
is_password_protected shows the cause.
End-to-end example: upload, then validate
A complete upload is three steps:
- Encode the whole file as a
data:URL (data:<mime-type>;base64,<content>). - Split that string into chunks and send each one to
POST /api/v1/preprocess. Every chunk carries the samerequestIdentifier— a UUID you generate — plus itschunkIndexand thetotalChunkscount. - Validate with
POST /api/v1/validate, passing{ "<requestIdentifier>": "<fileName>" }asfileIdentifiers. Thefile_idin the response is the id to pass in thefilesarray of Presentations and Posters requests.
Chunk the encoded string, not the raw file bytes — each chunk is a slice
of the same data: URL, and the server joins them back together in
chunkIndex order.
The script below does all three, with retries on each chunk. It needs
Python 3.8+ and requests (pip install requests).
#!/usr/bin/env python3
"""Upload a file to Prezent and print the file_id to use in generation requests.
Usage:
export PREZENT_API_KEY=your-api-key
python3 upload_file.py path/to/document.pdf
"""
import base64
import mimetypes
import os
import sys
import time
import uuid
from pathlib import Path
import requests
BASE_URL = "https://api.prezent.ai"
CHUNK_SIZE = 5 * 1024 * 1024 # characters of the encoded data URL per chunk
MAX_RETRIES = 3
HEADERS = {
"Authorization": f"Bearer {os.environ['PREZENT_API_KEY']}",
"Content-Type": "application/json",
}
def encode_as_data_url(path: Path) -> str:
"""Step 1: base64-encode the whole file as a data: URL."""
mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
content = base64.b64encode(path.read_bytes()).decode("utf-8")
return f"data:{mime_type};base64,{content}"
def upload_chunks(data_url: str, file_name: str) -> str:
"""Step 2: send the data URL to /preprocess in chunks. Returns the requestIdentifier."""
request_id = str(uuid.uuid4())
chunks = [data_url[i : i + CHUNK_SIZE] for i in range(0, len(data_url), CHUNK_SIZE)]
for index, chunk in enumerate(chunks):
payload = {
"fileContent": chunk,
"fileName": file_name,
"chunkIndex": index,
"totalChunks": len(chunks),
"requestIdentifier": request_id,
}
for attempt in range(1, MAX_RETRIES + 1):
try:
response = requests.post(
f"{BASE_URL}/api/v1/preprocess", headers=HEADERS, json=payload, timeout=60
)
if response.ok:
break
error = f"HTTP {response.status_code}: {response.text}"
except requests.RequestException as exc:
error = str(exc)
if attempt == MAX_RETRIES:
sys.exit(f"Chunk {index} failed after {MAX_RETRIES} attempts — {error}")
time.sleep(attempt) # back off before retrying
print(f"Uploaded chunk {index + 1}/{len(chunks)}")
return request_id
def validate(request_id: str, file_name: str) -> dict:
"""Step 3: validate the assembled file. Returns its validation result."""
response = requests.post(
f"{BASE_URL}/api/v1/validate",
headers=HEADERS,
json={"fileIdentifiers": {request_id: file_name}},
timeout=60,
)
response.raise_for_status()
return response.json()["data"]["files"][0]
def main() -> None:
if len(sys.argv) != 2:
sys.exit(__doc__)
path = Path(sys.argv[1])
data_url = encode_as_data_url(path)
request_id = upload_chunks(data_url, path.name)
result = validate(request_id, path.name)
if result.get("status") != "success":
reasons = [
label
for label, flag in (
("password protected", result.get("is_password_protected")),
("corrupted", result.get("is_corrupted")),
)
if flag
]
sys.exit(f"Validation failed: {', '.join(reasons) or result}")
print(f"Pages: {result.get('number_of_pages')}")
print(f"file_id: {result['file_id']}")
if __name__ == "__main__":
main()
Run it:
export PREZENT_API_KEY=your-api-key
python3 upload_file.py quarterly-review.pdf
Uploaded chunk 1/2
Uploaded chunk 2/2
Pages: 12
file_id: cbef85d9-d8ef-43c4-aa89-317f7d255bab
Then pass the file_id into a generation request:
{
"prompt": "Summarize the quarterly review for the leadership team",
"files": [{ "file_id": "cbef85d9-d8ef-43c4-aa89-317f7d255bab" }]
}
To upload several files, run steps 1 and 2 for each one, then validate
them together in one call — fileIdentifiers accepts one
requestIdentifier → fileName entry per file.
Errors
Common error codes:
400—MISSING_FILE_CONTENT,MISSING_REQUIRED_FIELD,INVALID_PAYLOAD,FILE_UPLOAD_FAILED.401—INVALID_API_KEY,EXPIRED_API_KEY,UNAUTHORIZED.404—ENDPOINT_NOT_FOUND,RESOURCE_NOT_FOUND.415—UNSUPPORTED_FILE_TYPE.422—INVALID_INPUT,UNPROCESSABLE_ENTITY.429—RATE_LIMIT_EXCEEDED,TOO_MANY_REQUESTS.500/503/504— standard server-side codes.
See the full catalog in Developer Guide → Error codes.