All paths below are relative to https://api.pdfcraft.ai/v1/pcc-api. Send Authorization: Bearer YOUR_API_KEY to every API request and Content-Type: application/json for JSON bodies. Responses use JSON and are not cacheable. OpenAPI provides a machine-readable specification.
For a working example, start with your first conversion. Field combinations are explained under conversion and translation.
Create a task
POST /transforms
| JSON field | Required | Description |
|---|---|---|
input_format | Yes | pdf or epub; immutable |
input_filename | No | Display name, 1–255 Unicode code points, not all whitespace; no control characters or invalid surrogate characters |
Unknown fields are rejected. input_filename cannot be null on creation. It does not set the download filename or the storage location.
{ "input_format": "pdf", "input_filename": "document.pdf"}Returns 201 and a task resource. Save uuid before uploading. Use the expiry time in the actual response.
{ "uuid": "8ae07e41-6e94-4144-9562-5af4116cba7e", "input_format": "pdf", "input_filename": "document.pdf", "status": "waiting_for_upload", "created_at": "2026-10-29T08:00:00.000Z", "updated_at": "2026-10-29T08:00:00.000Z", "expires_at": "2026-10-29T09:00:00.000Z"}Get a task
GET /transforms/{uuid} returns 200 and the task resource.
| Response field | Availability | Description |
|---|---|---|
uuid, input_format, status | Always | Identity, source format and lifecycle state |
created_at, updated_at | Always | ISO 8601 timestamps |
parameters | After accepted launch | Normalized, immutable launch parameters |
finished_at, expires_at | When available | Completion and service-owned expiry timestamps |
input_filename | If supplied and not cleared | Original display filename |
title, authors, language | When extracted and saved | Source-document metadata; authors is an array of names; language is not guaranteed to use API target-language codes |
statistics | When available | processed_pages and/or processed_words; not billing amounts or live percentages |
error | On a recorded error | Object containing code and message |
Optional fields may be absent. Read status from this endpoint; use observation for detailed live progress. Inaccessible, nonexistent and expired tasks return 404.
Rename the displayed source
PATCH /transforms/{uuid}
Accepts only input_filename, using the creation constraints. Set null to clear it, or omit it for no change. Valid in any accessible task state. Returns 200 with the updated resource.
{ "input_filename": "research.pdf"}Renaming does not replace the uploaded bytes, change conversion parameters or set the download filename.
Get upload URLs
GET /transforms/{uuid}/upload-urls
Returns 200. Before the source exists and before launch:
{ "put_url": "https://storage.example/source.pdf?signed", "headers": { "Content-Type": "application/pdf", "x-oss-forbid-overwrite": "true" }}PUT the raw file bytes using the returned headers, without API credentials. EPUB uses application/epub+zip. If the source already exists, the response instead contains:
{ "get_url": "https://storage.example/source.pdf?signed-get", "head_url": "https://storage.example/source.pdf?signed-head"}Uploaded source files cannot be overwritten. These URLs read the original file; use the download endpoint for conversion results. The example URLs on this page cannot be used for actual requests.
Launch processing
POST /transforms/{uuid}/launch
| JSON field | Required | Values / default |
|---|---|---|
output_format | Yes | markdown, epub, pdf |
target_language | For translation; mandatory for EPUB input or PDF output | One of the supported case-sensitive codes |
rendering_mode | No | source without translation; replace by default with translation; bilingual or bilingual-inline where supported |
translation_prompt | No | Nonblank string, only with translation |
footnotes | No; PDF only | disabled (default), basic, refined |
See valid combinations and language codes.
{ "output_format": "epub", "target_language": "en", "rendering_mode": "bilingual"}The service returns 202 when it first accepts the launch request. Repeating the same normalized parameters returns 200. Both responses include the complete task.
If the source is missing, the concurrency limit is reached, or you try to change accepted parameters, the service returns 409. Check error.code for the cause; see errors.
Download the result
GET /transforms/{uuid}/download-urls
When the task status is succeeded, the endpoint returns 200 and {get_url, head_url}. Other states return 404.
Download with get_url. To inspect file information with a HEAD request, use head_url. The URLs cannot be interchanged, and neither request requires an API key.
The optional filename query sets the download name, including its extension. Use URL encoding exactly once:
curl --fail-with-body --silent --show-error --get \ "$BASE/transforms/$TASK_UUID/download-urls" \ -H "Authorization: Bearer $PDF_CRAFT_API_KEY" \ --data-urlencode 'filename=research.markdown.zip'The name must not be blank, must fit in 255 UTF-8 bytes, and must not contain control characters, invalid surrogates, / or \, or equal . or ... Invalid or repeated filename queries return 400. The filename applies only to the generated links. It is not saved to the task and does not change the file contents. Include the extension yourself: .zip for Markdown results, .epub for EPUB, or .pdf for PDF. Use the returned URL as provided; its filename cannot be edited directly.
List tasks
| Endpoint | Contents | Ordering |
|---|---|---|
GET /transforms/waiting-for-upload/ | Unlaunched tasks, including uploaded sources | Creation time descending |
GET /transforms/running/ | Accepted work, including queued work | Creation time ascending |
GET /transforms/finished/ | Succeeded, failed, cancelled | Completion time descending |
Each accepts limit (1–100, default 50) and cursor. Ties are ordered by UUID. Treat cursors as opaque and reuse them only on the same queue.
{ "transforms": [ { "uuid": "8ae07e41-6e94-4144-9562-5af4116cba7e", "input_format": "pdf", "input_filename": "document.pdf", "status": "waiting_for_upload", "created_at": "2026-10-29T08:00:00.000Z", "updated_at": "2026-10-29T08:00:00.000Z", "expires_at": "2026-10-29T09:00:00.000Z" } ], "next_cursor": "opaque-cursor"}The example shows a waiting-list item. The last page omits next_cursor. Lists return basic resources without progress or download signatures. Expired tasks are hidden; see retention.
Observe progress and preview
GET /transforms/{uuid}/observation accepts optional cursor and positive integer preview_limit (default 500, values above 500 are clamped). Returns 200 with status, preview, optional running progress and successful download_urls.
GET /transforms/{uuid}/preview-assets/{stage}/{asset} returns {get_url, head_url} for a preview asset. stage is extraction or converting; asset is a 64-character lowercase hexadecimal hash followed by .png or .pdf.
These endpoints do not provide a full-document archive. See progress and preview for byte ranges, cursor advancement and content formats.