Skip to main content

Request and Response Transformation

Strip, rename, inject, and redact fields in a request or response — without changing the backend. This recipe covers three common transformation patterns: injecting headers before forwarding, reshaping the response body, and serving different response shapes per partner.

Typical scenario: Your backend API returns a verbose payload with internal field names. You need to expose a clean, versioned schema to partners — renaming fields, removing internal data, and injecting correlation headers — without changing the backend service.


Prerequisites​

  • You are signed in to the Management UI as an admin or editor
  • A collection and proxy exist (e.g. orders-api, GET /orders/{orderId})
  • An upstream service is reachable (e.g. https://orders.internal/v3/orders/{orderId})

Pattern A — Inject request headers before forwarding​

Inject a correlation header and an internal service token before the request reaches the upstream. The client does not need to know about these headers.

Workflow​

Steps​

  1. Open the proxy workflow canvas.
  2. Add a Set node after http_trigger. Name it inject-headers.
  3. Configure the output to build the enriched header map:
{
"X-Internal-Token": "{{env.INTERNAL_SERVICE_TOKEN}}",
"X-Correlation-ID": "{{trigger.headers['X-Request-ID'] || uuid()}}",
"X-Client-ID": "{{trigger.headers['X-Client-ID']}}",
"Authorization": "{{trigger.headers['Authorization']}}"
}
  1. Add an http_request_node, connect from inject-headers.
  2. Configure:
    • URL: https://orders.internal/v3/orders/{{trigger.params.orderId}}
    • Headers: use {{inject-headers.output}} as the headers object
  3. Connect a Response node to pass through {{upstream.response.body}} with status {{upstream.response.status}}.

Pattern B — Reshape the response body​

The upstream returns an internal schema. Transform it to a clean public schema before returning to the client.

Upstream response (internal schema):

{
"ord_id": "ORD-9182",
"cust_ref": "C-4421",
"line_items": [{ "sku": "P-001", "qty": 2, "unit_price_cents": 4999 }],
"ts_created": 1741526400,
"internal_region_code": "us-west-2a",
"fulfillment_partner_id": "FP-77"
}

Target public schema:

{
"orderId": "ORD-9182",
"customerId": "C-4421",
"items": [{ "sku": "P-001", "quantity": 2, "unitPrice": 49.99 }],
"createdAt": "2026-03-09T14:00:00Z",
"region": "us-west"
}

Steps​

  1. After the http_request_node, add a Code node. Name it transform-response.
  2. Write the transformation:
const raw = input.upstream.response.body;

return {
orderId: raw.ord_id,
customerId: raw.cust_ref,
items: (raw.line_items || []).map(item => ({
sku: item.sku,
quantity: item.qty,
unitPrice: item.unit_price_cents / 100
})),
createdAt: new Date(raw.ts_created * 1000).toISOString(),
region: raw.internal_region_code?.split('-').slice(0, 2).join('-') ?? null
// internal_region_code and fulfillment_partner_id are intentionally excluded
};
  1. Connect a Response node:
    • Status: 200
    • Body: {{transform-response.output}}
    • Content-Type: application/json
note

Fields internal_region_code and fulfillment_partner_id are dropped automatically by not including them in the output object. No explicit delete is required.


Pattern C — Serve different schemas per partner​

Partner A expects the v1 schema. Partner B expects v2. Use a condition node to branch on the incoming X-Profile-ID or X-API-Version header and apply the correct transformation.

Workflow​

Steps​

  1. After the http_request_node, add a Condition node. Name it check-version.
  2. Expression:
{{trigger.headers['X-API-Version']}} == "v2"

True branch (v2 transform) — Set node:

{
"orderId": "{{upstream.response.body.ord_id}}",
"customerId": "{{upstream.response.body.cust_ref}}",
"lineItems": "{{upstream.response.body.line_items}}",
"createdAt": "{{upstream.response.body.ts_created | toISO}}"
}

False branch (v1 transform) — Set node:

{
"order_id": "{{upstream.response.body.ord_id}}",
"customer_ref": "{{upstream.response.body.cust_ref}}",
"items": "{{upstream.response.body.line_items}}"
}
  1. Connect both branches to a single Response node.

Verify​

Pattern A — Header injection:

curl -i https://gateway.example.com/orders-api/orders/ORD-9182 \
-H "X-Client-ID: <client-id>" \
-H "Authorization: Bearer <token>" \
-H "X-Profile-ID: <profile-id>" \
-H "X-Request-ID: req-abc-123"

Confirm on your upstream service logs that X-Internal-Token and X-Correlation-ID were received.

Pattern B — Body transformation:

Confirm the response body contains orderId (not ord_id) and that internal_region_code is absent:

curl https://gateway.example.com/orders-api/orders/ORD-9182 ... | jq 'keys'
# Expected: ["createdAt","customerId","items","orderId","region"]
# Not present: internal_region_code, fulfillment_partner_id

Pattern C — Version-based schema:

# v2 schema
curl ... -H "X-API-Version: v2" | jq 'keys'
# Expected: ["createdAt","customerId","lineItems","orderId"]

# v1 schema (no version header)
curl ... | jq 'keys'
# Expected: ["customer_ref","items","order_id"]

Common transformation patterns​

GoalApproach
Rename a fieldset_node — map old key to new key
Drop sensitive fieldscode_node — return object without the field
Convert epoch to ISO 8601code_node — new Date(ts * 1000).toISOString()
Convert cents to decimalcode_node — price_cents / 100
Inject upstream auth headerset_node — map env.TOKEN into headers
Version-based schema switchcondition_node + two set_node branches
Wrap response in envelopeset_node — { data: ..., meta: { ... } }

Next steps​