Inkhmer LogoInKhmerBuilding the digital future
  • About
  • Projects
  • Blog
  • Tools
  • Learn
  • Contact
KMខ្មែរENEnglish

InKhmer.com — a personal Khmer Digital Hub curated by our team. Tools, Cambodia resources, Khmer culture, and digital projects. Practical, approachable, and built for everyone.

Social

FacebookTelegram

Quick Links

  • About
  • Projects
  • Blog
  • Tools
  • Learn
  • Contact

Explore

  • What We Build
  • How We Build
  • Roadmap
  • Experiments
  • Sitemap

Subscribe to our newsletter

Get the latest updates about Cambodia, new tools, and exclusive content delivered to your inbox.

  • Terms of Service
  • Privacy Policy
  • Cookie Policy

© 2026 InKhmer. All rights reserved.

  1. Home
  2. Developers
  3. Document Translator API
  4. getJobPdfUrl

Mint a presigned download URL (direct-from-storage)

GET/v1/public/document-translator/jobs/{jobId}/pdf-url

← All operations
translator:readrequired scope

Step 5 of the direct-download flow — returns a short-lived presigned GET `url` the client fetches straight from object storage (the bytes never transit the API). The TTL is tier-scoped (anonymous ≈5 min · free ≈15 min · paid-wallet ≈60 min · paid-api ≈15 min). Requires the SAME key the job was created with — any other key gets 404, never 403 (doc §9 existence-leak rule). Scope `translator:read` (10/min per key). Blocked jobs answer `403`.

Parameters

NameInRequiredDescription
jobIdpath✓The job id returned by `POST /jobs` (uuidv7).e.g. 019a2c3d-4e5f-7a9b-8c0d-1e2f3a4b5c6d

Error responses

200Presigned GET — fetch `url` directly from storage before `expiresAt`.

application/json

{
  "url": "https://storage.example.com/bkt/key?X-Amz-Expires=900&X-Amz-Signature=…",
  "method": "GET",
  "expiresAt": "2026-10-18T02:15:00Z",
  "filename": "translation-019a2c3d-4e5f-7a9b-8c0d-1e2f3a4b5c6d-km.pdf"
}
400Malformed request — missing field or bad value.

application/json

{
  "error": "invalid_request",
  "message": "`jobId` must be a UUID.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c"
}
401Missing or unrecognised API key.

application/json

{
  "error": "missing_authorization",
  "message": "Send `Authorization: Bearer <api-key>`.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c"
}
403The job exists but its download is blocked (abuse investigation or tier policy) — or the rolled-back server is in `DOWNLOAD_MODE=stream`, where only the legacy `GET /jobs/{jobId}/pdf` endpoint serves bytes.

application/json

{
  "error": "download_blocked",
  "message": "Downloads are blocked for this job.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c",
  "reason": "abuse",
  "blockedAt": "2026-10-18T02:00:00Z"
}
404The job does not exist — or exists under a different API key. Ownership misses deliberately return 404 (never 403) so job existence cannot be probed.

application/json

{
  "error": "job_not_found",
  "message": "No such job.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c"
}
429Per-key or per-account rate limit exhausted. Retry after the `Retry-After` interval (see the rate-limit table).

application/json

{
  "error": "rate_limited",
  "message": "Rate limit exhausted — retry after the interval.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c",
  "retryAfter": 42
}
500Engine failure — the wallet debit is refunded when one was taken.

application/json

{
  "error": "internal_error",
  "message": "Something went wrong — retry shortly.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c"
}
503A required upstream (OpenRouter, Bayla, key verification) is unavailable — the wallet debit is refunded when one was taken.

application/json

{
  "error": "upstream_unavailable",
  "message": "A required upstream is unavailable — retry shortly.",
  "requestId": "trk_01HKA1B2C3D4E5F6G7H8J9K0M1",
  "correlationId": "019a1b2c-3d4e-7f8a-9b0c-1d2e3f4a5b6c"
}