# Identify & translate

One capability, four faces, one contract: EPCIS 1.1, 1.2 or 2.0 XML in —
EPCIS 2.0 JSON-LD out, with a per-job round-trip fidelity report that names
every lossy path or certifies there were none. The hosted door, the CLI
verb, the MCP tool and the library function run the same engine; pick the
face your integration already speaks.

## POST /translate

| contract | value |
|---|---|
| Method, path | `POST https://epcis.dev/translate` |
| Auth | none — this door answers with no key and no account |
| Price class | free |
| Request | `application/xml` or `text/xml`: an EPCIS 1.1, 1.2 or 2.0 XML document |
| 200 response | `application/json`: `{ document, fidelityReport, meteredEvents, sourceSchemaVersion }` |
| Response headers | `GS1-EPCIS-Version`, `GS1-CBV-Version`, `GS1-Extensions`, `Spine-Translation-Fidelity`, `Spine-Translation-Lossy-Paths` |
| CLI twin | `npx epcis.dev translate <file.xml> [--out f.json]` |
| MCP twin | tool `translate` — answers with no key on the apex `POST /mcp` door and locally over stdio |
| Library twin | `import { translate, TranslationError } from "epcis.dev"` |

### Typed refusals

Errors are RFC 7807 `application/problem+json` carrying the standard's own
`epcisException:*` types. Match on `type`, never on prose.

| status | type | meaning |
|---|---|---|
| 400 | `epcisException:ValidationException` | translation failed; the fidelity report and per-path schema errors ride the problem body |
| 413 | `epcisException:CaptureLimitExceededException` | payload exceeds the file-size limit |
| 415 | `epcisException:UnsupportedMediaTypeException` | this door accepts `application/xml` or `text/xml`; POST JSON to `/validate` or `/hash` |

## Proof — run it against the live door

The fixture is EPCIS **1.1** XML, shipped in the npm tarball
(`node_modules/epcis.dev/golden-corpus/xml-translation/`). Executed against
the production door:

```
$ CORPUS=node_modules/epcis.dev/golden-corpus
$ curl -sS -D - -X POST https://epcis.dev/translate \
    -H 'content-type: application/xml' \
    --data-binary @$CORPUS/xml-translation/aggregation-event-1.1.xml \
    -o out.json
HTTP/2 200
gs1-cbv-version: 2.0.1
gs1-epcis-version: 2.0.1
gs1-extensions: spine=https://epcis.dev/ns/spine/v1
spine-translation-fidelity: round-trip-clean
spine-translation-lossy-paths: 0
```

And the body's receipt:

```json
{
  "sourceSchemaVersion": "1.1",
  "meteredEvents": 1,
  "fidelityReport": {
    "roundTripClean": true,
    "lossyPaths": [],
    "notes": "Clean round-trip: every source value maps to a standard EPCIS 2.0 field."
  }
}
```

The fidelity report is the door's own honesty mechanism: when a source
construct has no clean 2.0 home, `roundTripClean` goes `false` and
`lossyPaths` names each path — the door never silently drops data. The
expected-output pair for every shipped XML fixture sits beside it as
`*.expected.json`, so you can diff the door against the corpus yourself.

## Where the rest of the family is

The dated launch ledger at [/what-ships-today/](/what-ships-today/) is
authoritative for every door beyond this page. This reference lists exactly
the rows that answer, and nothing else.

---

**Proof, not adjectives.** This build could not verify the gateway's test suite green, so no sentence on this page claims a passing run — the last verified run is recorded in the repository's own CI, not here. Live on this origin, no key: POST /translate, /validate, /hash. No conformance attestation has ever been issued. The dated ledger is /what-ships-today/.
