Skip to content

Document Conversion API

Base path: /api/v1/pdf_handler/

Method Path Purpose
POST /convert/ Submit a conversion (multipart upload) → 202
POST /convert/json/ Submit a conversion (JSON body, URL or stored object) → 202
GET /conversion/{id}/ Poll conversion status
GET /{id}/download/ Download the result
GET /history/ List your recent conversions
GET /formats/ Supported formats
GET /operations/ Engines and options for one format pair
POST /save/ Save an edited PDF

Conversions are asynchronous: submit, poll, then download.

1. Submit

POST /convert/ takes multipart/form-data. Give exactly one input source — a file, a URL, or the key of an object already in indox storage.

curl -X POST https://indox.org/api/v1/pdf_handler/convert/ \
  -H "Authorization: Bearer $INDOX_TOKEN" \
  -F "[email protected]" \
  -F "target_formats=pdf,txt"
{
  "id": "6f1d8f0c-4c1f-4a1e-9a1b-0f2c5d6e7a8b",
  "status": "pending",
  "destination": "aws",
  "requested_formats": ["pdf", "txt"],
  "target_formats": ["pdf", "txt"],
  "service": "doc"
}

The status code is 202 Accepted. Keep id — every later call needs it.

Note

The generated OpenAPI schema for this route names the identifier job_id. The field the API actually returns is id. Read id.

Request fields

Field Type Notes
file file Multipart upload. One source only.
file_url string Fetch the input from a public URL. On /convert/json/ this field is named url.
s3_key string Key of an object already in indox storage.
target_formats string Comma-separated (pdf,docx) or a JSON list (["pdf","docx"]). Required, unless options is present.
destination enum aws (default), google_drive, dropbox, box, onedrive.
folder_path string Destination folder for Dropbox / OneDrive.
box_folder_id string Destination folder for Box.
redirect_url string Notified with the result when the job finishes. Must be a public URL.
engine string Force a converter (e.g. libreoffice, calibre, pandoc). Optional — the router picks one for you.
options JSON object PDF operations. See Editing a PDF.

Each requested format is charged separately, so pdf,txt costs two credits.

POST /convert/json/ accepts the same fields as a JSON body, for url or s3_key inputs. File uploads must use /convert/.

Repeated POST /convert/ calls create new conversions unless you send an Idempotency-Key header — with one, a retry returns the original conversion instead of starting a second. See Limits & errors.

2. Poll

curl https://indox.org/api/v1/pdf_handler/conversion/$ID/ \
  -H "Authorization: Bearer $INDOX_TOKEN"
{
  "conversion_id": "6f1d8f0c-4c1f-4a1e-9a1b-0f2c5d6e7a8b",
  "status": "completed",
  "requested_formats": ["pdf", "txt"],
  "successful_formats": ["pdf", "txt"],
  "failed_formats": [],
  "error_message": null,
  "warnings": [],
  "output": { "pdf": "/api/v1/pdf_handler/6f1d…/download/" },
  "outputs": [
    {
      "format": "pdf",
      "success": true,
      "file_size_bytes": 184320,
      "download_url": "/api/v1/pdf_handler/6f1d…/download/",
      "is_bundle": false
    }
  ],
  "download_url": "/api/v1/pdf_handler/6f1d…/download/",
  "bundle_download_url": null,
  "file_size_bytes": 184320,
  "created_at": "2026-08-04T09:12:03.114Z",
  "updated_at": "2026-08-04T09:12:11.902Z",
  "service": "doc"
}

Statuses

Status Terminal Meaning
pending no Queued, not started.
processing no A worker is on it.
completed yes Every requested format succeeded.
partial yes Some formats succeeded — read successful_formats / failed_formats.
failed yes Nothing was produced; error_message says why.

Stop polling on completed, partial, or failed. Poll every 2–5 s and back off exponentially past 60 s. download_url is null while there is no output, so treat its presence — not the status alone — as the signal to download.

When you request several formats, outputs may carry an extra entry with is_bundle: true: a ZIP of all results, linked from bundle_download_url.

3. Download

curl -L https://indox.org/api/v1/pdf_handler/$ID/download/ \
  -H "Authorization: Bearer $INDOX_TOKEN" \
  -o report.pdf

With destination=aws the endpoint streams the file back with a Content-Disposition filename. With a cloud destination it answers with a redirect to the provider's link, so follow redirects (-L) either way.

400 file not ready means the conversion has no output yet — keep polling.

Listing past conversions

GET /history/?page=1&page_size=20 returns your own conversions, newest first. page_size is clamped to 1–200. The response carries conversions, total_count, page, page_size, and has_next.

See Errors and limits for status codes, size caps, and how long results are kept.