Request and answer, both ways
The request is reshaped for the target, and a synchronous target’s answer is reshaped back into the shape your caller expects, on the same call.
API transformation
Your system speaks one shape and the partner’s API another. Today that gap is a service someone wrote and someone maintains. Describe the mapping once, both ways, and EchoRelay runs it, so the next field the partner renames is an edit, not a deployment.
Worked example
Your app submits a support request, but your help desk expects a nested ticket object. EchoRelay builds that object and maps the response back into your app's format. These examples use fictional APIs.
Configure one synchronous target: EchoRelay waits for your partner's response and returns the mapped body to your caller in the same request.
{
"title": "Cannot sign in",
"reporterEmail": "user@example.com"
}
{
"ticket": {
"subject": "Cannot sign in",
"requester": {
"email": "user@example.com"
}
}
}
The request mapping puts title inside ticket.subject and
reporterEmail inside ticket.requester.email.
outgoingMapping){
"$schema": "https://schemas.echorelay.dev/transform/mapping/v1.json",
"root": {
"type": "object",
"id": "request-root",
"properties": {
"ticket": {
"type": "object",
"id": "ticket",
"properties": {
"subject": {
"type": "field",
"id": "subject",
"path": "title"
},
"requester": {
"type": "object",
"id": "requester",
"properties": {
"email": {
"type": "field",
"id": "requester-email",
"path": "reporterEmail"
}
}
}
}
}
}
}
}
{
"ticket": {
"id": "T-42",
"state": "open"
}
}
{
"ticketId": "T-42",
"status": "open"
}
The response mapping reads ticket.id and ticket.state
and returns a flat object with ticketId and status.
answerMapping){
"$schema": "https://schemas.echorelay.dev/transform/mapping/v1.json",
"root": {
"type": "object",
"id": "response-root",
"properties": {
"ticketId": {
"type": "field",
"id": "ticket-id",
"path": "ticket.id"
},
"status": {
"type": "field",
"id": "status",
"path": "ticket.state"
}
}
}
}
If your help desk changes subject to title, you
rename that property in the request mapping and publish the updated configuration.
Your app keeps sending the same support request.
Your monitor reports a response time in milliseconds, but your reporting API expects seconds. The request mapping divides the value by 1,000 and renames the service field.
{
"service": "search",
"durationMs": 1310
}
{
"service_name": "search",
"duration_seconds": 1.31
}
This mapping keeps two decimal places. It declares halfUp rounding:
a value halfway between two results rounds away from zero.
outgoingMapping){
"$schema": "https://schemas.echorelay.dev/transform/mapping/v1.json",
"root": {
"type": "object",
"id": "monitoring-root",
"properties": {
"service_name": {
"type": "field",
"id": "service-name",
"path": "service"
},
"duration_seconds": {
"type": "arithmetic",
"id": "duration-seconds",
"op": "divide",
"outputPrecision": 2,
"roundingMode": "halfUp",
"left": {
"type": "field",
"id": "duration-ms",
"path": "durationMs"
},
"right": {
"type": "constant",
"id": "milliseconds-per-second",
"value": 1000
}
}
}
}
}
Why EchoRelay
The request is reshaped for the target, and a synchronous target’s answer is reshaped back into the shape your caller expects, on the same call.
Dry-run a mapping against a sample, see the diff against what is live, and publish while traffic keeps flowing. Rolling back shows you the change before it applies.
Hand an OpenAPI document or a sample payload to your agent over MCP and it writes the mapping; the editor in the dashboard is there for people.
Questions
No credit card required.