Skip to content

Response Extraction

MirApi can extract specific fields from upstream responses and reshape the JSON structure — or redirect to a URL found inside the response body — without any changes to your application code.

Two headers control response transformation:

  • X-Extract-Redirect — Find a URL in the response body and redirect to it (302 Found)
  • X-Extract-Map — Extract and rename fields from the response body into a new JSON object

Extracts a URL from the upstream response body using a JSONPath expression (or a fallback chain with ||) and issues a 302 Found redirect to that URL.

Upon a successful response from the upstream (HTTP status code 2xx), the proxy analyzes the response body, looking for a value at the specified JSONPath. If a value is found, the proxy interrupts the standard response transmission to the client and instead returns an HTTP redirect 302 Found pointing to that URL.

  • Fallback chain support: The expression supports logical OR (||). E.g., X-Extract-Redirect: $.url || $.checkoutUrl || $.data.payment_link. The proxy will check each path in sequence and redirect to the first one that returns a non-empty value.
  • Automatic type casting: If the found node is not a string (for example, a number or a boolean), it will be automatically converted to a string.
  • Triggers only on 2xx: If the upstream returns an error (e.g. 400 or 500), no redirect is executed so that the client can inspect the original upstream error response.
FeatureDetails
$. prefixOptional — prepended automatically if omitted
Array notation []Supported — extracts the redirect URL from matching array elements

This is useful for payment flows where the upstream returns a checkout URL in the response body and you need to redirect the user to it:

Terminal window
# Upstream returns: {"session_id": "cs_123", "url": "https://checkout.stripe.com/pay/cs_123"}
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/checkout/sessions" \
-H "X-Identity-Key: Bearer sk_live_..." \
-H "X-Extract-Redirect: url" \
-H "Content-Type: application/json" \
-d '{"mode": "payment", "line_items": [...]}'
# $. is prepended automatically — same as X-Extract-Redirect: $.url
# → 302 Found
# → Location: https://checkout.stripe.com/pay/cs_123

Different payment providers use different field names for the checkout URL. Use || to try multiple paths in order. The $. prefix is optional for every term:

Terminal window
# Works across Stripe, Adyen, PayU, and other providers
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.payment-provider.com/sessions" \
-H "X-Extract-Redirect: url || checkoutUrl || data.payment_link || redirect_url" \
-H "Content-Type: application/json" \
-d '{"amount": 9900, "currency": "usd"}'
# MirApi tries each path in order — first non-null value wins
# → 302 Found with the discovered URL
# Works the same with a nested path
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.adyen.com/checkout/v68/sessions" \
-H "X-Extract-Redirect: action.url || url" \
-H "Content-Type: application/json" \
-d '{"amount": {"currency": "EUR", "value": 1000}, "reference": "order-123"}'

Use [] brackets to extract a redirect URL from elements inside an array:

Terminal window
# Upstream returns: {"data": {"orders": [{"id": "ord_1", "checkout_url": "https://pay.example.com/ord_1"}]}}
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.provider.com/checkout" \
-H "X-Extract-Redirect: data.orders[].checkout_url" \
-H "Content-Type: application/json" \
-d '{"order_ref": "ord_1"}'
# → 302 Found
# → Location: https://pay.example.com/ord_1

Build a single endpoint that works with multiple payment providers without knowing which field name each one uses:

Terminal window
# No matter which provider is in your cascade route, X-Extract-Redirect finds the URL
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: payment-cascade" \
-H "X-Extract-Redirect: url || checkoutUrl || data.payment_link" \
-H "Content-Type: application/json" \
-d '{"amount": 9900, "currency": "usd"}'

Response mapping parses the upstream service’s response JSON, extracts fields using JSONPath, and constructs a brand-new JSON payload for the client. Only fields explicitly listed in the rules are returned — all other response fields are discarded (whitelist principle).

It intercepts the upstream server’s JSON response and transforms its structure before returning it to the client.

  • Multiple Mapping via Comma: Rename/extract multiple fields at once by separating rules with a comma.
    • Example: X-Extract-Map: $.id=>charge_id, $.status=>state
  • Nested Structures Creation: The target key (targetKey) supports dot-notation to generate nested JSON objects dynamically.
    • Example: $.id=>data.charge.id will generate {"data": {"charge": {"id": "..."}}}.
  • Working with Arrays/Lists: Transform entire arrays of objects using empty brackets [].
    • Example: provider_data.orders[].order_id=>data.orders[].order
  • Value Interpolation: Format output values by wrapping them in a text template using the syntax target_key(Template {value}). You can also reference sibling fields from the same nesting level of the JSON context.
    • Example (Value Interpolation): provider_data.order_id=>data.order_text(Order #{value}) transforms ID 123 to {"data":{"order_text":"Order #123"}}.
    • Example (Cross-field): status=>status_text(Order {order_id} is {value}) will interpolate the order_id field from the same level of nesting.
  • Single Field Fallback (without =>): If you pass a JSONPath without a target key (e.g. X-Extract-Map: $.status), the proxy returns a JSON object with the extracted value in a default key named extracted.
    • Example: {"extracted": "success"}

The source (left side) and target (right side) of => share a unified syntax:

FeatureDetails
$. prefix on sourceOptional — prepended automatically if missing
Array notation on sourceUse [] brackets instead of JSONPath wildcards [*]
Nested target keysDot-notation supported (e.g. data.order)
Multiple array queriesMerged into a single array by element index
Value template {value}Formats the extracted value inside a custom string
Cross-field template {field_name}Interpolates other fields from the same array element into the output

Extracts a field and maps it to a new flat key. The $. prefix is optional:

Terminal window
curl https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.provider.com/orders" \
-H "X-Extract-Map: provider_data.order_id=>order_id"
# Upstream: {"provider_data": {"order_id": 353454876}}
# Returns: {"order_id": "353454876"}
# Extract the caller's IP from httpbin
curl https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://httpbin.org/get" \
-H "X-Extract-Map: $.origin"
# Returns: {"extracted": "93.123.45.67"}

Creates nested JSON structures in the client output using dot-notation on the right side:

Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.provider.com/orders" \
-H "X-Extract-Map: provider_data.order_id=>data.order" \
-H "Content-Type: application/json" \
-d '{"ref": "ord_999"}'
# Upstream: {"provider_data": {"order_id": 353454876}}
# Returns: {"data": {"order": "353454876"}}
# Normalize Stripe response
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.stripe.com/v1/charges" \
-H "X-Identity-Key: Bearer sk_live_..." \
-H "X-Extract-Map: $.id=>charge_id, $.status=>payment_status, $.amount=>amount_cents, $.created=>created_at" \
-H "Content-Type: application/json" \
-d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'
# Returns: {"charge_id": "ch_3Pz9...", "payment_status": "succeeded", "amount_cents": 2000, "created_at": 1748130000}

Extracts arrays from upstream and places them under custom keys. Use [] brackets to iterate every element:

Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.provider.com/orders" \
-H "X-Extract-Map: provider_data.orders[].order_id=>data.orders[].order" \
-H "Content-Type: application/json" \
-d '{"customer": "cus_123"}'
# Upstream: {"provider_data": {"orders": [{"order_id": 111}, {"order_id": 222}]}}
# Returns: {"data": {"orders": [{"order": "111"}, {"order": "222"}]}}

Multiple rules targeting the same array path are merged by element index into a single combined array:

Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.provider.com/orders" \
-H "X-Extract-Map: provider_data.orders[].order_id=>data.orders[].order, provider_data.orders[].amount=>data.orders[].sum" \
-H "Content-Type: application/json" \
-d '{"customer": "cus_123"}'
# Upstream: {"provider_data": {"orders": [{"order_id": 111, "amount": 100}, {"order_id": 222, "amount": 200}]}}
# Returns: {"data": {"orders": [{"order": "111", "sum": "100"}, {"order": "222", "sum": "200"}]}}
# AI provider normalization
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: ai-providers" \
-H "X-Extract-Map: $.choices[0].message.content=>text, $.model=>model_used" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
# Returns: {"text": "Hello! How can I help?", "model_used": "gpt-4o"}

E. Value Templating & Cross-Field Array Templating

Section titled “E. Value Templating & Cross-Field Array Templating”

Append a template string in parentheses after the target key name. Use {value} as the placeholder for the extracted field, and {field_name} to reference any sibling field within the same array element:

Terminal window
# Single-field value template
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Target-URL: https://api.provider.com/orders" \
-H "X-Extract-Map: provider_data.orders[].order_id=>data.orders[].order_id, provider_data.orders[].status=>data.orders[].status_text(Order {order_id} is {value})" \
-H "Content-Type: application/json" \
-d '{"customer": "cus_123"}'
# Upstream: {"provider_data": {"orders": [
# {"order_id": 111, "status": "success"},
# {"order_id": 222, "status": "fail"}
# ]}}
# Returns: {"data": {"orders": [
# {"order_id": "111", "status_text": "Order 111 is success"},
# {"order_id": "222", "status_text": "Order 222 is fail"}
# ]}}

Template placeholder rules:

  • {value} — the extracted value of the source field
  • {field_name} — any sibling field already mapped to the same array element

X-Extract-Redirect and X-Extract-Map serve different purposes and cannot be combined in the same request. Use one or the other:

HeaderUse when…Response
X-Extract-RedirectUpstream returns a URL to redirect to302 Found with Location header
X-Extract-MapYou want a trimmed/renamed JSON response200 OK with transformed JSON body