The adapter you don't have to deploy

September 8, 2026

Your system emits one JSON shape. The vendor you are integrating with wants another. Their field isorder_id, yours isorderId. Their amounts are strings, yours are numbers. Their status vocabulary is not your status vocabulary.

The usual answer is a small service. A function, a container, something with a repository and a deploy and a place in your on-call rotation, whose entire job is renaming fields.

You do not need it. The translation is configuration on the target, and you can watch it run against a real payload before it ever sees traffic.

Two things a target can reshape

The first is the request you send it. Set an outgoing mapping on the target and the caller's body arrives in the shape that target expects, whichever way the target is delivered.

{
  "$schema": "https://schemas.echorelay.dev/transform/mapping/v1.json",
  "root": {
    "type": "object",
    "id": "root",
    "properties": {
      "order_id": { "type": "field", "id": "order-id", "path": "orderId" },
      "source": { "type": "constant", "id": "src", "value": "echorelay" }
    }
  }
}

A caller's{"orderId": "9F2"}reaches that target as{"order_id": "9F2", "source": "echorelay"}.

The second is the answer coming back, and only on a synchronous target. That is the one case where your caller's connection is still open when the vendor replies, so it is the only place there is a reply left to rewrite. A streaming target is never reshaped: a mapping needs a whole document, and a stream does not have one until it ends.

Mappings live on the target, not the endpoint. Fan one request out to three vendors and each one gets the shape it asked for, from the same caller payload, with no branching anywhere in your code.

When the vendor answers more than one way

Most vendors do not have a single reply shape. There is a success envelope and an error envelope, a 200 and a 429. Answer variants let one target carry a mapping per form: pick one discriminator, the response status or one header or one body field, and give a mapping for each value. A value can match exactly, or by class, or fall through to a default. They resolve by specificity, so the order you write them in does not matter.

What happens when nothing matches is a choice you make per target, not a default we picked for you. Either the vendor's answer passes through untouched, or the request fails. There is no third behaviour where something quietly guesses.

One case worth knowing about: the vendor that replies 200 with an error buried inside the body. A variant can answer your caller with a status of your own choosing, so your caller learns something went wrong from the status line rather than by parsing a body. That is your contract with your own caller and nothing more. What we measure and what you are billed stay the delivery's actual outcome.

A tree, not a script

A mapping is a tree of typed nodes, written as JSON. The visual builder in the panel and a hand written or agent written document are two views of the same tree: build it one way and it opens unchanged in the other.

It can pull a value out of a nested path, build a new object shape, work across arrays by mapping each element, filtering, counting, summing, joining or flattening, branch when a field is missing or switch on a value, concatenate and split, change case, run arithmetic with the output precision and rounding mode you name rather than one we chose for you, reformat a date, and map one vocabulary onto another.

Every value is read from a fixed snapshot of the input, so no node can read another node's output. That sounds like a restriction and it buys you something concrete: the whole tree is checked in one pass, so you get every rule that fails at once instead of fixing them one error at a time.

Run it before you publish it

Paste a sample request or a sample answer and the dry run evaluates your mapping against it, showing which node produced each field in the output, or exactly which node and which value failed. The panel's Try it button, the dry run over the REST API, and the dry run tool over MCP all run the same evaluation. Nothing reaches the vendor, nothing is queued, and it is not billed separately from the delivery it belongs to.

A mapping also runs against a fixed budget, counted per node evaluated and per byte produced, the same on every plan. That same preview reports what your tree spends against it, so an expensive mapping shows its cost before it goes live rather than after.

When it fails, it says so

A mapping is compiled once, not on every request. If it compiles but cannot be evaluated against a real request or a real answer, because a field it expected is missing or a value does not fit, the request is rejected with a 422 that names the target and every node that failed.

The vendor's untouched body is never sent as a fallback for a mapping that was supposed to run and did not. A translation that half happened is worse than one that stopped and told you.

What you actually save

Every adapter you do not deploy is a repository nobody maintains, a dependency nobody patches, and one less thing that can be down separately from the integration it exists to serve.

Reshaping a payload is one of the few parts of an integration that is genuinely just data. It should not need a runtime of its own. The full reference, with worked examples of every node type, is inthe mapping docs.

Connect everything. Deploy nothing.

No credit card required.