Base workflow
- Confirm your partner identity and limits with
GET /api/v1/account.
- Check backlog and recent activity with
GET /api/v1/jobs/summary.
- Create a full one-step job with
POST /api/v1/jobs or a partial workflow with POST /api/v1/workflows.
- Optionally reconcile recent jobs with
GET /api/v1/jobs.
- Inspect the job timeline with
GET /api/v1/jobs/{job_id}/events.
- Poll
GET /api/v1/jobs/{job_id} or wait for a webhook.
- Inspect step outputs with
GET /api/v1/jobs/{job_id}/artifacts.
- Inspect webhook delivery with
GET /api/v1/jobs/{job_id}/webhook.
- Optional: cancel an active job with
POST /api/v1/jobs/{job_id}/cancel.
- Optional: purge a terminal job with
DELETE /api/v1/jobs/{job_id}.
- Download the completed file from
GET /api/v1/jobs/{job_id}/result.
Important defaults
- Server-to-server only. It is not designed for browser-side API keys.
POST /api/v1/jobs always runs the full 1,2,3,4 pipeline.
POST /api/v1/workflows accepts 1 to 3 ascending steps such as 1,3 or 2,3,4.
url is supported only when Step 1 is requested and accepts a public HTML page or a direct public PDF URL.
- Workflows that start at Step 2, Step 3, or Step 4 must upload a Markdown file.
- Supported outputs:
docx, html, pdf.
Authentication
Send the partner API key in the Authorization header:
Authorization: Bearer tlapi_...
API keys are provisioned by running python scripts/create_partner_api_key.py ...
against the target environment.
Schema and Interactive Docs
The live app also exposes machine-readable and interactive docs generated
from the FastAPI routes:
/openapi.json: machine-readable OpenAPI schema
/docs: interactive Swagger UI
Treat this page as the human-readable public guide. Use
/docs for live request/response schemas and
/openapi.json for client generation.
If you generate a client SDK, prefer the stable partner operation IDs:
getPartnerAccount, createPartnerJob,
createPartnerWorkflow,
listPartnerJobs, getPartnerJobSummary,
getPartnerJobEvents, getPartnerJob,
listPartnerJobArtifacts, getPartnerJobArtifact,
getPartnerJobWebhookStatus, cancelPartnerJob,
purgePartnerJob, and getPartnerJobResult.
Inspect Your Partner Account
curl -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/account
This returns the authenticated partner ID, display name, configured rate
limit, concurrent-job limit, last-used timestamp, and convenience links to
the live docs.
Response fields:
partner_id, name, contact_email, active
rate_limit_per_minute, concurrent_job_limit, last_used_at
api_docs_url, openapi_url
Get a Job Summary
curl -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/summary
This returns partner-scoped counts by public job status, recent 24-hour
activity, and the current available concurrency derived from your configured
job limit.
Response fields:
counts.total, counts.active, counts.queued, counts.processing, counts.complete, counts.error, counts.cancelled
recent_24h.window_hours, recent_24h.created, recent_24h.completed, recent_24h.errored, recent_24h.cancelled
concurrent_job_limit, available_concurrency
Create a Full One-Step Job
curl -X POST https://dev.translation.legal/api/v1/jobs \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: retry-20260329-001" \
-F file=@contract.pdf \
-F source_lang=th \
-F target_lang=en \
-F output_format=docx \
-F external_job_id=partner-123
Required form fields:
source_lang
target_lang
output_format
- Exactly one of
file or url
Optional form fields:
external_job_id
webhook_url
Accepted sources:
file: uploaded document input supported by the current Step 1 profile
url: either a public HTML page or a direct public PDF URL
Direct PDF URLs are downloaded through the same bounded intake path used
for uploaded PDFs and remain subject to the same size and page limits.
Private or internal redirect targets are rejected.
Create a Partial Workflow
curl -X POST https://dev.translation.legal/api/v1/workflows \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: retry-20260417-001" \
-F steps=1,3 \
-F file=@contract.pdf \
-F source_lang=th \
-F target_lang=en \
-F external_job_id=partner-124
Supported step patterns:
1, 1,2, 1,3, 1,4
1,2,3, 1,2,4, 1,3,4
2, 2,3, 2,4, 2,3,4
3, 3,4, 4
Rules:
- Steps must be ascending and must not repeat.
- Maximum 3 steps.
- Use
POST /api/v1/jobs for the full 1,2,3,4 pipeline.
- If Step 1 is included, send exactly one of
file or url.
- If Step 1 is omitted, upload a Markdown file in
file.
source_lang is required for Step 1, Step 2, or Step 3.
target_lang is required for Step 3.
output_format is required only for Step 4.
If a workflow includes Step 1 but skips Step 2, the job surfaces a warning
that OCR cleanup was skipped before downstream processing.
Safe Retries / Idempotency
If your network times out after calling POST /api/v1/jobs,
retry with the same Idempotency-Key header instead of
submitting a brand-new request.
- Same partner + same key + same request payload: the original job is reused
- Same partner + same key + different payload: the API returns
409
- Replay responses include
idempotency_replay=true
Use short, unique retry keys such as retry-20260329-001. The
API accepts letters, numbers, dots, underscores, colons, and hyphens.
List and Reconcile Jobs
curl -H "Authorization: Bearer $API_KEY" \
"https://dev.translation.legal/api/v1/jobs?status=complete,cancelled&limit=20&offset=0"
curl -H "Authorization: Bearer $API_KEY" \
"https://dev.translation.legal/api/v1/jobs?external_job_id=partner-123"
curl -H "Authorization: Bearer $API_KEY" \
"https://dev.translation.legal/api/v1/jobs?created_from=2026-03-28&created_to=2026-03-29"
Supported query parameters:
status: optional comma-separated public statuses: queued, processing, complete, error, cancelled
external_job_id: optional exact match against your partner-supplied identifier
created_from / created_to: optional inclusive created-at bounds using ISO dates or datetimes
limit: optional page size, default 20, max 100
offset: optional zero-based offset for pagination
The response includes jobs, count, total,
limit, offset, and has_more.
Inspect Job Events
curl -H "Authorization: Bearer $API_KEY" \
"https://dev.translation.legal/api/v1/jobs/$JOB_ID/events?limit=100"
curl -H "Authorization: Bearer $API_KEY" \
"https://dev.translation.legal/api/v1/jobs/$JOB_ID/events?level=WARNING&limit=50"
The event timeline is built from the stored task logs for the parent
pipeline and any child step tasks. Each event includes a timestamp, level,
message, and source label such as pipeline or step2.
Supported query parameters:
level: optional exact log level filter
limit: optional max events, default 100, max 500
Status and Result
curl -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs
curl -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID
curl -H "Authorization: Bearer $API_KEY" \
"https://dev.translation.legal/api/v1/jobs/$JOB_ID/events?limit=100"
curl -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID/webhook
curl -X POST -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID/cancel
curl -X DELETE -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID
curl -L -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID/result \
-o output.docx
Wait for status=complete and output_ready=true
before calling the result endpoint. If the file is not ready yet, the
result endpoint returns 409.
Job status now also includes workflow_type,
requested_steps, completed_steps, and
per-step artifacts. Artifact ids are stable labels such as
step1, step2, step3, and
step4.
Each artifact entry includes:
artifact_id, step_number, artifact_type
filename, content_type, download_url
status, is_final
curl -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID/artifacts
curl -L -H "Authorization: Bearer $API_KEY" \
https://dev.translation.legal/api/v1/jobs/$JOB_ID/artifacts/step3 \
-o translated.md
Cancellation is supported for queued or in-progress jobs. Cancelling a
completed or failed job returns 409, and a cancelled job will
no longer expose result_url.
Purge is supported for terminal jobs only. Once a purge succeeds, later
status and result requests for that job return 404.
Webhook Signing
When webhook_url is supplied, terminal job updates are signed
with the partner’s webhook secret.
X-Translation-Legal-Event: job.completed, job.cancelled, or job.failed
X-Translation-Legal-Timestamp: Unix timestamp
X-Translation-Legal-Signature: sha256=<hex>
Signature input:
timestamp + "." + raw_json_body
Verify signatures in Python:
import hashlib
import hmac
def verify_signature(webhook_secret: str, timestamp: str, raw_body: bytes, header_value: str) -> bool:
expected = hmac.new(
webhook_secret.encode("utf-8"),
f"{timestamp}.".encode("utf-8") + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(header_value, f"sha256={expected}")
Verify signatures in Node.js:
import crypto from "node:crypto";
export function verifySignature(webhookSecret, timestamp, rawBody, headerValue) {
const expected = crypto
.createHmac("sha256", webhookSecret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(headerValue),
Buffer.from(`sha256=${expected}`)
);
}
You can inspect the last recorded delivery state for a job with
GET /api/v1/jobs/{job_id}/webhook. The response includes whether
a webhook was configured and the last recorded delivery state for terminal
webhook events.
Retention
The current cleanup policy keeps partner-facing final outputs, task logs,
and terminal task records for about 30 days. Temporary intermediates are
retained for shorter windows, typically around 3 days.
If you need earlier deletion for a specific partner job, use
DELETE /api/v1/jobs/{job_id} after that job is terminal.
Treat the API as an asynchronous processing interface, not as permanent
document storage.
Understanding Review Warnings
Review metadata is split into English explanations and source-language
evidence:
warnings: short English warning strings
review_findings: structured English review issues
review_targets: source-language snippets copied from the original document
If review_targets contains Thai or another source script, that
does not mean the API is returning untranslated UI text. Those snippets are
included so a reviewer can compare the flagged references against the
original source PDF.
Errors to Expect
400: invalid input, unsupported language pair, unsupported output format
401: missing or invalid API key
404: job not found or not owned by this partner
409: result not ready yet, job cancelled, or an Idempotency-Key was reused with a different payload
429: rate limit or concurrent-job limit reached
503: required conversion tooling is unavailable on the server