software · Smoke Workspace

How to use Rodmena® PDF API

Version 2Updated 6 Sept 2026

pdfapi — zero-retention document-to-PDF conversion API

Base URL: https://pdfapi.rodmena.co.uk · version 0.1.0 · OpenAPI: https://pdfapi.rodmena.co.uk/openapi.json

Everything you need is below; the response shapes are exact.

What this is. Upload a document, get a document id, ask for a read token, poll status, download the PDF once. Then it is gone. Nothing is retained: the source is deleted the moment conversion finishes, the PDF is deleted the moment it is downloaded (or explicitly deleted, or 24 h after upload, whichever comes first), and no log line ever carries content, filenames, credentials or tokens.

1. Two credentials, two jobs

Credential Where Used on
Basic auth — username = your client_id, password = your rak_… API key Authorization: Basic base64(client_id:rak_…) upload, token issue, delete, key management
Document token — a per-document bearer capability you request after upload Authorization: Bearer <token> status, download

Status and download need ONLY the document token, so you can hand that token to whoever needs the PDF without sharing your API key. Tokens expire after 15 minutes (never beyond the document itself); request another whenever you like.

Never put a token or key in a URL. They travel in the Authorization header only.

2. Getting a client (self-service)

curl -X POST https://pdfapi.rodmena.co.uk/v1/clients -H 'Content-Type: application/json' \
  -d '{"name": "my-app", "email": "you@example.com"}'
# -> 201 {"client_id": "c_01J…", "api_key": "rak_…", "key_id": "…", "note": "…"}

The api_key is shown exactly once. Registration is unauthenticated and capped per source IP and globally per day; a 429 here means wait for Retry-After.

Key lifecycle (Basic auth):

GET    /v1/clients/me/keys              -> {"client_id", "keys": [{key_id, key_prefix, label, is_active, …}]}
POST   /v1/clients/me/keys  {"label"?}  -> 201 {"client_id", "key_id", "api_key", "key_prefix"}
DELETE /v1/clients/me/keys/{key_id}     -> {"key_id", "revoked", "already_revoked"}

A revoked key stops working within 30 seconds (the verifier caches positive verdicts for that long, never negative ones).

3. The flow

# 1. upload (multipart; the part's Content-Type declares the format, or send a `format` field)
curl -u "$CLIENT_ID:$API_KEY" -X POST https://pdfapi.rodmena.co.uk/v1/documents \
  -F 'file=@invoice.html;type=text/html' -F 'page_size=A4'
# -> 201 {"document_id": "01J…", "state": "queued", "expires_at": "…Z",
#         "status_url": "…/v1/documents/01J…", "token_url": "…/v1/documents/01J…/token"}

# 2. token
curl -u "$CLIENT_ID:$API_KEY" -X POST https://pdfapi.rodmena.co.uk/v1/documents/$DOC/token
# -> 201 {"token": "…", "expires_at": "…Z", "document_id": "01J…"}

# 3. poll
curl -H "Authorization: Bearer $TOKEN" https://pdfapi.rodmena.co.uk/v1/documents/$DOC
# -> 200 {"document_id", "state": "queued|processing|ready|failed|retrieved|deleted|expired",
#         "format", "created_at", "expires_at", "error": null | {"code", "detail"},
#         "attempts", "pages", "output_bytes"}

# 4. download — ONCE
curl -H "Authorization: Bearer $TOKEN" -o out.pdf --fail https://pdfapi.rodmena.co.uk/v1/documents/$DOC/pdf
# -> 200 application/pdf  (Content-Disposition: attachment; filename="<document_id>.pdf")

# optional: purge early (Basic)
curl -u "$CLIENT_ID:$API_KEY" -X DELETE https://pdfapi.rodmena.co.uk/v1/documents/$DOC   # -> 204

Poll every 2 s; conversion usually finishes in a few seconds for HTML and under 30 s for office documents. The /pdf route answers 409 not_ready with Retry-After while the job is queued or processing — deliberately not a 2xx, so curl --fail -o out.pdf never saves a JSON body as a PDF.

The download is a single read. The moment /pdf answers 200 the file is unlinked on the server. If your connection drops mid-transfer, the document is gone and you re-upload. There is no second chance, by design.

Idempotent uploads. Send Idempotency-Key: <your unique key> on the upload; a repeat within 24 h with the same key returns the same document (200 with Idempotent-Replayed: true) and is charged once.

4. Formats and rendering options

Declared type Also accepted as format= Engine
text/html, application/xhtml+xml html, htm Chromium (print media, A4 default)
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx LibreOffice
application/msword doc LibreOffice
application/vnd.oasis.opendocument.text odt LibreOffice
application/rtf, text/rtf rtf LibreOffice
text/plain txt LibreOffice
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx LibreOffice
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx LibreOffice
application/pdf pdf pikepdf normalise (strips OpenAction/JavaScript/embedded files)

The declared type and the file’s actual bytes must agree, or you get 415 unsupported_media_type. application/octet-stream without a format field is refused. Encrypted PDFs (owner or user password) are refused with 422 invalid_document.

Multipart fields for HTML rendering: javascript (default false), page_size (A3|A4|A5|Letter|Legal|Tabloid), landscape (false), margin_mm (15, 0–50), print_background (true), scale (1.0, 0.1–2.0).

HTML documents render with NO network access. Every external resource — images, stylesheets, fonts, scripts, iframes — is blocked; only inline content and data: URIs render. Embed what you need. This is what makes it safe to send us untrusted HTML, and it is not configurable.

5. Limits

  • Upload cap: 20 MiB per document (413 payload_too_large, also enforced at the edge).

  • Output cap: 50 MiB / 500 pages (a larger result is a 422 conversion_failed).

  • Render timeouts: HTML 30 s, office 90 s, PDF 20 s.

  • Retention: 24 h maximum from upload; tokens 15 min.

  • Quotas and rate limits are per client and answered as 429 with Retry-After; when the queue or ephemeral storage is full you get 503 queue_full / storage_full with Retry-After and nothing is charged.

6. Errors — RFC 7807 problem+json with a stable code

{"type": "https://pdfapi.rodmena.co.uk/problems/not-ready", "title": "The PDF is not ready yet",
 "status": 409, "code": "not_ready", "state": "processing"}

Branch on code, not on prose:

status code meaning
400 invalid_multipart, invalid_page_options, invalid_idempotency_key fix the request
401 unauthenticated, invalid_credentials Basic credentials missing or rejected (WWW-Authenticate: Basic)
401 token_invalid, token_expired document token missing/unknown/expired (WWW-Authenticate: Bearer)
404 document_not_found unknown id, or not your document
409 not_ready still queued/processing — honour Retry-After
409 key_limit_reached 25 active keys; revoke one
410 document_gone already retrieved, deleted or expired (state says which)
413 payload_too_large over the 20 MiB cap
415 unsupported_request_content_type body is not multipart/form-data
415 unsupported_media_type declared type unsupported or disagrees with the bytes
422 validation_error see errors
422 invalid_document empty, encrypted, zip bomb, unparsable
422 conversion_failed the renderer rejected the input (not refunded)
429 rate_limited, quota_exceeded wait for Retry-After / reset_at
500 internal_error our fault; a failed conversion of this kind is refunded
503 backend_unavailable a dependency is down; we fail closed, never unmetered or unauthenticated
503 queue_full, storage_full back off for Retry-After; nothing was charged

A failed document’s status carries error.code = conversion_failed (your input) or internal_error (ours, refunded). GET /pdf on it answers 422 or 500 accordingly.

7. Service

Path Auth Notes
`GET HEAD /healthz, /ping` no
`GET HEAD /readyz` no
`GET HEAD /llms.txt` no
`GET HEAD /robots.txt` no
GET /docs, /openapi.json no interactive docs

Every response carries X-Request-Id (quote it when reporting a problem) and X-Robots-Tag: noindex.

Document record

Publication
software
Publisher
Smoke Workspace
Version
2
Last updated
This document
https://kb.rodmena.co.uk/docs/rodmena/software/how-to-use-rodmena-pdf-api

Published from Knowledge Base by RODMENA LIMITED. This is the current published version of this document and may be revised.

Back to top