Skip to content

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-MTmlt, ga-IEgai, da-DKda. 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); a PATCH on an item then replaces body (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-progress either — 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.