Pitfalls & gotchas¶
The API is straightforward once you know these. Each cost real debugging time.
1. Language codes are proprietary, not BCP-47¶
Eurotext doesn't use de-DE/en-US. Its codes are mostly lowercase BCP-47 — but
not always: mt-MT → mlt, ga-IE → gai, da-DK → da. Use
LanguageMap::toEurotext() for the common set and GET /info/languages as the
authoritative source. An unmapped code fails item creation.
(05-language-codes.md)
2. __meta takes flat key-value pairs only¶
Values inside body["__meta"] must be simple key-value values — string, number,
or boolean. Numbers are fine (the spec's own example is
{"id_in_your_system": 132}). Nested arrays/objects, null, and JSON-encoded
structures are rejected. Split routing data into separate flat keys instead. The
client enforces this before sending.
(07-metadata-roundtrip.md)
3. textType is a fixed set — and it grows¶
Nine values today: specialized-text, product, term, marketing,
template, software, ai-01, ai-02, ai-03. Don't hardcode the list —
ai-0x were added after the first six. Pull it from GET /info/text-types.
(06-text-types.md)
4. order vs quote¶
POST /project needs a type. order commissions the translation; quote asks
for a price estimate first. Pick deliberately — it's a business decision, not a
default to ignore. The client defaults to quote; pass type: 'order' when you
mean it.
5. Status lives in elements, and it's grouped¶
GET /project/{id} has no single status field. Items are grouped by state under
elements (each group has items, maybe files). Done = every item in
finished; failure shows in error. A group may omit the files key entirely.
Use ProjectStatus::fromProject(). (08-status-model.md)
6. Translation vs. source¶
When you read an item back, the translated fields are under translation; the
original stays under body. Read from translation.
7. Success codes aren't all 200¶
Creates return 201, transitions and deletes return 204, reads return 200.
Guzzle treats all 2xx as success, so it doesn't matter to the client — but if
you're checking status codes by hand, don't hardcode 200.
8. The lifecycle is one-way — add all items before the transition¶
The order create → add items → transition is mandatory, not a convention:
- Items can only be created/edited while the project is a draft (
status: default); aPATCHon an item then replacesbody(no merge). - After the transition the project is
locked: no more items can be added, and existing items can't be changed. - You still can't add items once items are
in-progresseither — locked is locked.
Forgot an item? There's no "reopen" — start a new project for it. Full flow:
03-workflow.md.
9. Delivery is slow — poll or use the webhook¶
Human translation takes hours to days. Never block a request on it. Poll from a
cron/queue job, or register a webhook url at project creation.
(11-webhook-and-polling.md)
10. No server-side dedupe¶
Send the same product twice and you get two projects. There's no idempotency key —
duplicate protection is your job. (12-idempotency.md)
11. Clean up abandoned projects — but only before work starts¶
If a push fails partway, DELETE /project/{id} so you don't accumulate orphans.
This only works while the project is a draft or freshly transitioned — once its
items are in-progress, the project can no longer be deleted. So do cleanup
immediately on failure, not later. See the try/catch in
examples/submit_and_poll.php.
12. Stage vs. production¶
Stage: https://stage.api.eurotext.de/api/v2. Production:
https://api.eurotext.de/api/v2. Develop against stage; payloads are identical.