TrueIdiom REST API

TrueIdiom is a human-in-the-loop machine translation platform. You submit source text or a source document; the service produces a translation, scores it against a quality gate, and either publishes it autonomously or parks it for a human reviewer. Approved work is written to a durable golden store and fed back into the tenant's translation memory, so the same content costs less — and eventually nothing — to translate again.

The API is JSON over HTTPS, served under a flat /api prefix, and versioned by date through an optional api-version query parameter. Layout-preserving PDF/DOCX/XLSX translation runs as an asynchronous job; text translation runs inline and returns the finished job in the response.

Production base URLhttps://trueidiom.com
Path prefix/api — there is no /v1 segment
API version?api-version=2026-09-01 — optional; omitting it serves the oldest supported version. See Versioning
Content typeapplication/json, except uploads (multipart/form-data), the OAuth2 endpoints (application/x-www-form-urlencoded), and file downloads

Liveness

curl -sS https://trueidiom.com/healthz
{"status": "ok", "stub_mode": {"translator": false, "refiner": false}, "live_services": true}

Unauthenticated, and not under /api. stub_mode reports whether live translation services are configured for this deployment. Treat live_services: false as a hard signal that translations from that deployment are not real output — see Conventions.


Quickstart

Four calls: get a credential, translate something, read the job back, download the result. Every command below is copy-pasteable as written (jq is used only to pull ids out of responses).

1. Get a tenant API key

Self-service signup creates a tenant and returns its API key. The key is returned exactly once — capture it now.

export TRUEIDIOM_API_KEY=$(curl -sS -X POST "https://trueidiom.com/api/auth/signup/email?api-version=2026-09-01" \
  -H "Content-Type: application/json" \
  -d '{
        "tenant_name": "Acme Localization",
        "email": "you@example.com",
        "password": "correct-horse-battery-staple"
      }' \
  | jq -r .tenant_api_key)

echo "$TRUEIDIOM_API_KEY"

Passwords must be at least 12 characters and must not be a common choice or your email local-part. Full request and response schema: Authentication → POST /api/auth/signup/email.

2. Submit one translation

POST /api/translations runs the whole pipeline inline, so this call returns the finished job. Allow ~120 seconds of client timeout; do not treat a slow response as a failure.

export JOB_ID=$(curl -sS -X POST "https://trueidiom.com/api/translations?api-version=2026-09-01" \
  -H "X-API-Key: $TRUEIDIOM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-001" \
  -d '{
        "source_text": "Contact support before disabling two-factor authentication.",
        "source_lang": "en",
        "target_lang": "fr",
        "domain": "software"
      }' \
  | jq -r .id)

echo "$JOB_ID"

3. Read the job back

The job is already terminal when step 2 returns, but you will re-read it from another process — to check whether a human is required, or after a reviewer has acted.

curl -sS "https://trueidiom.com/api/translations/$JOB_ID?api-version=2026-09-01" \
  -H "X-API-Key: $TRUEIDIOM_API_KEY" \
  | jq '{status, final_translation, decision: .agent_run.quality_gate.decision}'
{
  "status": "published",
  "final_translation": "Contactez l'assistance avant de désactiver l'authentification à deux facteurs.",
  "decision": "pass"
}

status: "published" means the quality gate cleared it. status: "awaiting_review" means a human is required — submit a decision with POST /api/translations/{job_id}/review, documented in Text translation.

Two things in this API are genuinely polled, and no webhooks notify you about job completion — poll the job resources instead. (The one webhook in the platform is the inbound Stripe receiver, which notifies us about billing events; it is not a callback you can subscribe to.) The two are: asynchronous document jobs — GET /api/documents/{document_id} until status is translated or failed, see Documents — and large TMX imports, where POST /api/tm answers 202 and you poll GET /api/tm/imports/{job_id}, see Terminology & TM.

4. Download the result

curl -sS "https://trueidiom.com/api/translations/$JOB_ID/golden?format=txt&api-version=2026-09-01" \
  -H "X-API-Key: $TRUEIDIOM_API_KEY"
Contactez l'assistance avant de désactiver l'authentification à deux facteurs.

format also accepts docx and pdf. Only published and human_edited jobs are downloadable; anything else returns 409.


Authentication at a glance

Auth is enforced by server-side dependencies, not declared as OpenAPI security schemes. The generated /docs page therefore shows every endpoint as if it were public. It is not — these pages are the authority.

MethodSent asUse it forScope
Tenant API keyX-API-Key: <key> (or Authorization: Bearer <key>)Server-to-server integration: translation, documents, terminology, TM, storage, billing checkoutOne tenant. Does not expire.
User access tokenAuthorization: Bearer <access_token>Acting on behalf of a signed-in person; the only credential that carries a user identity and a roleOne user inside one tenant. Default lifetime 3600 s.
Operator API keyX-API-Key: <key>Platform operations and support toolingCross-tenant. Break-glass only — do not use it for integration traffic.
Session cookie (ll_session)Cookie: ll_session=<access_token>Browser navigations that cannot set headersSame as the access token it carries. API clients should ignore it.

X-Tenant-ID: <tenant_id> is optional on any of these and pins credential resolution to one tenant.

The trap to know about: global_administrator is a per-tenant role — every tenant creator holds it — and it does not grant cross-tenant reads. Only the operator API key does. A signed-in admin listing jobs, documents, usage, or users sees only their own tenant. On the id-addressed routes another tenant's id returns 404, never 403 — the id is simply not found in your scope. GET /api/auth/users is the exception in form, not in spirit: a foreign ?tenant_id is silently ignored and you get your own roster back, since a listing route can refuse by answering with your own data. The one deliberate 403 is the SSO org-join refusal, where a workspace with domain auto-join off turns away a new user matching its domain.

Consistent variable names across these pages: $TRUEIDIOM_API_KEY (tenant API key), $TRUEIDIOM_ACCESS_TOKEN (user access token), $TRUEIDIOM_OPERATOR_KEY (operator key).

Full details, including the MFA round-trip and the Google sign-in flow: Authentication & Authorization.


Reference pages

PageWhat is in it
Conventions, errors & limitsBase URLs, credential headers, the guard matrix for every operation, JSON and datetime rules, identifier formats, pagination, payload limits, the canonical error catalog, idempotency, retry guidance, versioning. Read this once before you write code.
Authentication & AuthorizationEvery /api/auth/* and /api/oauth2/* endpoint: signup, the three token grants, MFA enrolment and challenges, token revoke and introspect, user listing and role changes, Google sign-in.
Text translationPOST /api/translations and its four accepted request shapes, job listing and detail, human review, golden download, the TranslationJob schema, the quality gate, batch submission.
Document translationAsynchronous PDF/DOCX/XLSX translation in place: upload, poll, walk segments, post corrections, approve, download; artifacts, failure taxonomy, and the full document schema set.
Terminology & translation memoryGlossary and TMX upload, listing, deletion and export; polling a large TMX import; the golden TMX feed; how terminology is enforced and how a 100% TM match returns with no translation call at all.
Bring Your Own StorageBinding a tenant's own Azure Data Lake Gen2 account: read, replace, verify, delete; what lands in customer storage; the fail-closed errors this produces on translation endpoints.
Tenants, usage & billingTenant provisioning, per-day and per-period usage, ledger export, Stripe Checkout and Customer Portal, the webhook receiver, quotas and the free-trial grant.

Core concepts

Tenant. The unit of isolation and billing — one customer workspace, holding its own jobs, glossaries, translation memory, usage ledger and storage binding. Every request resolves to exactly one tenant, and nothing is ever visible across tenants except to the platform operator key. The web app calls this a workspace; on the wire it is always tenant (X-Tenant-ID, tenant_id, missing or invalid tenant credentials), and these pages use "tenant" throughout.

Job. One unit of translation work and its complete audit trail: the request, the agent run that executed it, quality evidence, review state, and the final translation. Text jobs (TranslationJob, from /api/translations) complete inline; document jobs (TenantDocumentJob, from /api/documents) run asynchronously and are polled to translated or failed.

Segment. The addressable unit of a document — one text region, or one non-empty cell of a table. GET /api/documents/{document_id}/segments is the authoritative walk of what counts as a segment; corrections are posted back against a segment's region_id (and cell_id for a table cell).

Terminology set. A tenant-scoped glossary of source_term → target_term pairs, uploaded as CSV or TMX. Matched terms are enforced through the translation and checked in the output afterwards, so a non-compliant translation cannot publish autonomously no matter how high it scores.

Translation memory (TM). A tenant-scoped store of bilingual segments, either imported from TMX or accumulated from approvals. It serves two tiers: an exact tier that returns a 100% match verbatim with no translation call at all, and a fuzzy tier where close matches from your approved work guide the new translation.

Golden store. The system of record for approved work — one durable record per approved job, plus document-approval audit records. It is written on approval and read back through GET /api/translations/{job_id}/golden. Distinct from the golden TM set (golden-approved-<src>-<tgt>), which holds only the bilingual pairs and is what makes later translations cheaper.

Quality score. A 0100 judgement of a text translation, on quality_assessment.score, compared against a threshold (default 85; override per request with metadata.min_quality_score). Document jobs use a separate 01 overall_score on the quality report — the two scales are not interchangeable.

Human review. The gate that decides whether a translation ships or waits for a person. It defers to a human when the score is below threshold, terminology is non-compliant, a critical issue is present, or the request is marked sensitive/regulated; the decision is submitted with POST /api/translations/{job_id}/review for text, or POST /api/documents/{document_id}/review plus /approve for documents.


The generated OpenAPI document

The openapi.json document is dumped from the running FastAPI application — 57 paths and 61 component schemas — and is also served live at GET /openapi.json, rendered as Swagger UI at GET /docs. Use it for schema shapes and for client generation.

It is not the authority on authentication. The generated document declares no securitySchemes and no global security requirement, so the interactive /docs page shows every endpoint as unauthenticated. Auth is enforced by server-side dependencies instead, and never described to OpenAPI. The practical consequence: /docs will happily let you fire requests that the server rejects with 401, and a generated client will contain no auth wiring at all. Take the auth requirements from these pages — the guard matrix covers every operation — and add the credential headers to your client yourself.

Two further gaps worth knowing: the multipart upload endpoints (POST /api/documents/translate, POST /api/terminology, POST /api/tm) declare no requestBody because they read the form directly, and POST /api/translations declares an untyped body because it dispatches on Content-Type. Those request shapes are documented on their endpoint pages.