Eurotext Translation API v2 — AI Integration Primer¶
API v2.0.0 · verified against Stage 2026-07-31
How to use: copy the fenced block below into your AI coding assistant (Claude / Copilot / Cursor / …), then tell it which language/framework to use. It encodes the API contract and the mistakes AI codegen usually makes, and asks the assistant to verify its own output before showing it to you.
You are helping me integrate the Eurotext Translation API v2. Follow this contract
EXACTLY when generating code. Generate the client in the language/framework I specify;
if I haven't specified one, ask first.
# Environments
- Stage: https://stage.api.eurotext.de/api/v2
- Production: https://api.eurotext.de/api/v2
- Each environment needs its OWN key (a stage key fails on prod). Configure both;
never hardcode secrets — read them from env.
# Auth
- Header: Authorization: Bearer <token> (note the SPACE after "Bearer")
- Content-Type: application/json
# Mandatory workflow (this ORDER is enforced by the API)
1. Create project — POST /project (required: type = "order" | "quote", and name)
2. Add ALL items — POST /project/{id}/item (one item = one sourceLanguage -> one targetLanguage)
3. Start translation — PATCH /transition/project/{id} body {"status":"new"}
4. Wait — poll or receive a webhook (translation takes hours to days)
5. Fetch results — GET /project/{id}/item/{itemId}
After step 3 the project is LOCKED: no items can be added or edited, and there is no
reopen. Add everything before transitioning.
# Endpoints (method · path · success code)
- POST /project -> 201
- POST /project/{id}/item -> 201
- PATCH /transition/project/{id} -> 204 (start {"status":"new"} · cancel a draft {"status":"trash"})
- GET /project/{id} -> 200 (status lives in `elements`, see rules)
- GET /project/{id}/item/{itemId} -> 200
- DELETE /project/{id} -> 204 (draft only)
- GET /info/languages -> 200 (authoritative language codes)
- GET /info/text-types -> 200 (authoritative textType values)
# MUST / MUST-NOT
- The transition endpoint is /transition/project/{id} — NOT /project/{id}/item/transition.
- Language codes are PROPRIETARY, not BCP-47 (e.g. de-de, en-us; exceptions like
mt-MT->mlt, ga-IE->gai, da-DK->da). Resolve via GET /info/languages; never assume the
standard tag.
- textType is a fixed, growing set (product, marketing, software, ai-01, …). Pull from
GET /info/text-types; do not hardcode.
- __meta is a reserved key INSIDE `body` for data that must not be translated (your ids).
It accepts FLAT SCALARS ONLY (string/number/boolean) — no nested objects/arrays/null.
`body` itself MAY be nested.
- Reading status: GET /project/{id} has NO single status field. Items are grouped under
`elements` (with `elementStatuses`). Done = every item in `finished`; failure = not all
finished and the `error` group is non-empty. Never poll for a top-level status === "finished".
- Reading results: translated fields are under `translation`, which MIRRORS `body`
structurally (same keys, same nesting, same __meta) with translated leaf values. It is
[] until finished. Read it GENERICALLY: walk the keys, skip __meta, map each leaf back
via the ids you stored in __meta. Do not assume a fixed field set.
- Success codes vary: 201 create, 204 transition/delete, 200 reads. Handle 204 (empty
body). Never hardcode === 200.
- DELETE works only while the project is a draft (status: default). After the transition
it is locked and cannot be deleted.
- No server-side dedupe and no idempotency key — deduplicate on your side and make
write-back repeat-safe.
- Polling: at most every ~30 minutes, from a cron/queue job (never a web request).
Prefer a webhook: pass `url` on create, treat the callback as a "something changed"
trigger, and re-read the project for authoritative state.
# Shapes (illustrative)
Create request:
{ "type": "order", "name": "My project", "url": "https://my.app/webhook" }
Add item:
{ "sourceLanguage": "de-de", "targetLanguage": "en-us", "textType": "product",
"body": { "name": "Roter Bürostuhl", "__meta": { "id": 132 } } }
Finished item (translation mirrors body; body may be nested):
{ "id": 132, "status": "finished", "sourceLanguage": "de-de", "targetLanguage": "en-us",
"body": { "__meta": { "id": 132 }, "content": { "headline": "…source…" } },
"translation": { "__meta": { "id": 132 }, "content": { "headline": "…translated…" } } }
# Your task
Generate a client in my stack that:
- configures Stage and Production (separate base URLs + keys from env),
- runs the create -> add-all-items -> transition flow,
- retrieves results via polling (cron/queue, >=30 min, backoff, deadline) OR a webhook
handler that re-reads the project,
- reads results generically from `translation` via __meta,
- handles 400 / 401 / 404 distinctly (parse the 400 message).
Ask me for my language codes and textType, or call the /info endpoints — never guess.
# Self-check (do this BEFORE showing me any code)
Verify your generated code against every item below; fix silently until each holds. Then
output ONE line — "Contract compliance: ✓" — and name only the items you had to fix. Do
NOT print this list. If an item needs data you don't have, call /info or ask me — never guess.
1. Transition URL is /transition/project/{id}, not /project/{id}/item/transition.
2. Order: project -> ALL items -> transition; no add/edit after the transition.
3. type (order/quote) is sent on create.
4. Language codes are not assumed BCP-47; sourced from /info/languages, config, or a question.
5. textType from /info/text-types or config; not guessed/hardcoded.
6. __meta holds flat scalars only, with a guard against nested/objects/null. body may be nested.
7. Status read by aggregating elements/elementStatuses (done = all finished; failure = not all
finished and error non-empty); no top-level status === "finished".
8. Results read from `translation` (not body); translation === [] means "not ready"; walk keys
generically; skip __meta; route via __meta ids.
9. Success codes 201/204/200 all handled; 204 empty body handled; no hard === 200.
10. "Bearer " with a space; 401/404/400 handled distinctly (400 message parsed).
11. Polling >=30 min in cron/queue (not a web request) with backoff + deadline — or a webhook
handler that re-reads the project.
12. Caller-side dedupe; write-back repeat-safe (no reliance on server dedupe).
13. Separate base URL + key per environment; secrets from env, not hardcoded.
14. DELETE only while draft (status: default).
15. Sane timeout; no unbounded retry loop; retries only with backoff; log request/status/error-body.