> For the complete documentation index, see [llms.txt](https://docs.digitalapi.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.digitalapi.ai/api-gateway/plugin-reference/plugins-transformation.md).

# Transformation plugins

Plugins in this category rewrite the request or response on the fly: add or remove headers, swap parameters, change the body, fold or split JSON. Attach them when the backend expects a slightly different shape than the consumer is sending or vice versa. The catalogue ships 13 Transformation plugins.

{% hint style="info" %}
**In this category:** [`attach-consumer-label`](#attach-consumer-label), [`body-transformer`](#body-transformer), [`degraphql`](#degraphql), [`fault-injection`](#fault-injection), [`grpc-transcode`](#grpc-transcode), [`grpc-web`](#grpc-web), [`json-to-xml`](#json-to-xml), [`jsonp-wrapper`](#jsonp-wrapper), [`mocking`](#mocking), [`pgp-crypto`](#pgp-crypto), [`proxy-rewrite`](#proxy-rewrite), [`response-rewrite`](#response-rewrite), [`xml-to-json`](#xml-to-json)
{% endhint %}

### `attach-consumer-label`

The `attach-consumer-label` plugin attaches custom consumer-related labels, in addition to `X-Consumer-Username` and `X-Credential-Indentifier`, to authenticated requests, for upstream services to differentiate between consumers and implement additional logics.

{% hint style="info" %}
**Priority** `2399` · **Category** Transformation · **Required** `headers`
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name    | Type   | Required | Default | Valid values | Description                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------- | ------ | -------- | ------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| headers | object | True     |         |              | Key-value pairs of consumer labels to be attached to request headers, where key is the request header name, such as `X-Consumer-Role`, and the value is a reference to the custom label key, such as `$role`. Note that the value should always start with a dollar sign (`$`). If a referenced consumer value is not configured on the consumer, the corresponding header will not be attached to the request. |

**Sample configuration**

Attach a single consumer label as a request header:

```json
{
  "attach-consumer-label": {
    "headers": {
      "X-Consumer-Role": "$role"
    }
  }
}
```

Attach several labels at once:

```json
{
  "attach-consumer-label": {
    "headers": {
      "X-Consumer-Role": "$role",
      "X-Consumer-Tier": "$tier",
      "X-Consumer-Department": "$department"
    }
  }
}
```

</details>

### `body-transformer`

The `body-transformer` Plugin performs template-based transformations to transform the request and/or response bodies from one format to another, for example, from JSON to JSON, JSON to HTML, or XML to YAML.

{% hint style="info" %}
**Priority** `1080` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name                          | Type    | Required | Default | Valid values                                         | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ----------------------------- | ------- | -------- | ------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `request`                     | object  | False    |         |                                                      | Request body transformation configuration.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `request.input_format`        | string  | False    |         | \[`xml`,`json`,`encoded`,`args`,`plain`,`multipart`] | Request body original media type. If unspecified, the value would be determined by the `Content-Type` header to apply the corresponding decoder. The `xml` option corresponds to `text/xml` media type. The `json` option corresponds to `application/json` media type. The `encoded` option corresponds to `application/x-www-form-urlencoded` media type. The `args` option corresponds to GET requests. The `plain` option corresponds to `text/plain` media type. The `multipart` option corresponds to `multipart/related` media type. If the media type is neither type, the value would be left unset and the transformation template will be directly applied. |
| `request.template`            | string  | True     |         |                                                      | Request body transformation template. The template uses the gateway's template engine syntax. See the template syntax for more details. You can also use auxiliary functions `_escape_json()` and `_escape_xml()` to escape special characters such as double quotes, `_body` to access request body, and `_ctx` to access context variables.                                                                                                                                                                                                                                                                                                                          |
| `request.template_is_base64`  | boolean | False    | false   |                                                      | Set to true if the template is base64 encoded.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `response`                    | object  | False    |         |                                                      | Response body transformation configuration.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `response.input_format`       | string  | False    |         | \[`xml`,`json`]                                      | Response body original media type. If unspecified, the value would be determined by the `Content-Type` header to apply the corresponding decoder. If the media type is neither `xml` nor `json`, the value would be left unset and the transformation template will be directly applied.                                                                                                                                                                                                                                                                                                                                                                               |
| `response.template`           | string  | True     |         |                                                      | Response body transformation template.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `response.template_is_base64` | boolean | False    | false   |                                                      | Set to true if the template is base64 encoded.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

**Sample configuration**

Transform the request body from form-encoded input into a JSON payload for the upstream:

```json
{
  "body-transformer": {
    "request": {
      "input_format": "encoded",
      "template": "{\"name\":\"{{_escape_json(name)}}\",\"email\":\"{{_escape_json(email)}}\"}"
    }
  }
}
```

Transform the upstream JSON response into a different JSON shape for the client:

```json
{
  "body-transformer": {
    "response": {
      "input_format": "json",
      "template": "{\"id\":\"{{id}}\",\"fullName\":\"{{first}} {{last}}\"}"
    }
  }
}
```

Transform both request and response in a single configuration:

```json
{
  "body-transformer": {
    "request": {
      "input_format": "xml",
      "template": "{\"order\":\"{{order_id}}\"}"
    },
    "response": {
      "input_format": "json",
      "template": "<order><id>{{order_id}}</id></order>"
    }
  }
}
```

</details>

### `degraphql`

The `degraphql` Plugin is used to support decoding RESTful API to GraphQL.

{% hint style="info" %}
**Priority** `509` · **Category** Transformation · **Required** `query`
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name            | Type   | Required | Description                                                                                  |
| --------------- | ------ | -------- | -------------------------------------------------------------------------------------------- |
| query           | string | True     | The GraphQL query sent to the upstream                                                       |
| operation\_name | string | False    | The name of the operation, is only required if multiple operations are present in the query. |
| variables       | array  | False    | The variables used in the GraphQL query                                                      |

**Sample configuration**

Minimal query that maps a REST call onto a GraphQL query:

```json
{
  "degraphql": {
    "query": "query { users { id name } }"
  }
}
```

Query with variables passed through from the request:

```json
{
  "degraphql": {
    "query": "query($id: ID!) { user(id: $id) { id name email } }",
    "variables": ["id"]
  }
}
```

Named operation when the query document defines more than one:

```json
{
  "degraphql": {
    "query": "query GetUser($id: ID!) { user(id: $id) { id } } query ListUsers { users { id } }",
    "operation_name": "GetUser",
    "variables": ["id"]
  }
}
```

</details>

### `fault-injection`

The `fault-injection` Plugin can be used to test the resiliency of your application. This Plugin will be executed before the other configured Plugins.

{% hint style="info" %}
**Priority** `11000` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

The `abort` attribute will directly return the specified HTTP code to the client and skips executing the subsequent Plugins.

The `delay` attribute delays a request and executes the subsequent Plugins.

| Name               | Type     | Requirement | Default | Valid       | Description                                                                                                                        |
| ------------------ | -------- | ----------- | ------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| abort.http\_status | integer  | required    |         | \[200, ...] | HTTP status code of the response to return to the client.                                                                          |
| abort.body         | string   | optional    |         |             | Body of the response returned to the client. gateway variables like `client addr: $remote_addr\n` can be used in the body.         |
| abort.headers      | object   | optional    |         |             | Headers of the response returned to the client. The values in the header can contain gateway variables like `$remote_addr`.        |
| abort.percentage   | integer  | optional    |         | \[0, 100]   | Percentage of requests to be aborted.                                                                                              |
| abort.vars         | array\[] | optional    |         |             | Rules which are matched before executing fault injection. See the gateway's expression syntax for a list of available expressions. |
| delay.duration     | number   | required    |         |             | Duration of the delay. Can be decimal.                                                                                             |
| delay.percentage   | integer  | optional    |         | \[0, 100]   | Percentage of requests to be delayed.                                                                                              |
| delay.vars         | array\[] | optional    |         |             | Rules which are matched before executing fault injection. See the gateway's expression syntax for a list of available expressions. |

**Sample configuration**

Abort every request with a fixed status and body:

```json
{
  "fault-injection": {
    "abort": {
      "http_status": 503,
      "body": "Service temporarily unavailable"
    }
  }
}
```

Abort only a percentage of requests to simulate intermittent failures:

```json
{
  "fault-injection": {
    "abort": {
      "http_status": 500,
      "percentage": 20
    }
  }
}
```

Delay requests to simulate a slow backend:

```json
{
  "fault-injection": {
    "delay": {
      "duration": 2,
      "percentage": 50
    }
  }
}
```

Combine an abort and a delay in one configuration:

```json
{
  "fault-injection": {
    "abort": {
      "http_status": 429,
      "percentage": 10
    },
    "delay": {
      "duration": 1.5,
      "percentage": 30
    }
  }
}
```

</details>

### `grpc-transcode`

The `grpc-transcode` Plugin converts between HTTP and gRPC requests.

{% hint style="info" %}
**Priority** `506` · **Category** Transformation · **Required** `proto_id`, `service`, `method`
{% endhint %}

<details>

<summary>Configuration and details</summary>

Helix takes in an HTTP request, transcodes it and forwards it to a gRPC service, gets the response and returns it back to the client in HTTP format.

| Name                   | Type                                                      | Required | Default | Description                                                                                                                                                                                                                                            |
| ---------------------- | --------------------------------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| proto\_id              | string/integer                                            | True     |         | id of the the proto content.                                                                                                                                                                                                                           |
| service                | string                                                    | True     |         | Name of the gRPC service.                                                                                                                                                                                                                              |
| method                 | string                                                    | True     |         | Method name of the gRPC service.                                                                                                                                                                                                                       |
| deadline               | number                                                    | False    | 0       | Deadline for the gRPC service in ms.                                                                                                                                                                                                                   |
| pb\_option             | array\[string([pb\_option\_def](#options-for-pb_option))] | False    |         | protobuf options.                                                                                                                                                                                                                                      |
| show\_status\_in\_body | boolean                                                   | False    | false   | Whether to display the parsed `grpc-status-details-bin` in the response body                                                                                                                                                                           |
| status\_detail\_type   | string                                                    | False    |         | The message type corresponding to the [details](https://github.com/googleapis/googleapis/blob/b7cb84f5d42e6dba0fdcc2d8689313f6a8c9d7b9/google/rpc/status.proto#L46) part of `grpc-status-details-bin`, if not specified, this part will not be decoded |

| Type            | Valid values                                                                              |
| --------------- | ----------------------------------------------------------------------------------------- |
| enum as result  | `enum_as_name`, `enum_as_value`                                                           |
| int64 as result | `int64_as_number`, `int64_as_string`, `int64_as_hexstring`                                |
| default values  | `auto_default_values`, `no_default_values`, `use_default_values`, `use_default_metatable` |
| hooks           | `enable_hooks`, `disable_hooks`                                                           |

**Sample configuration**

Minimal transcode of an HTTP request onto a gRPC method:

```json
{
  "grpc-transcode": {
    "proto_id": "1",
    "service": "helloworld.Greeter",
    "method": "SayHello"
  }
}
```

Set a deadline and protobuf options:

```json
{
  "grpc-transcode": {
    "proto_id": "1",
    "service": "helloworld.Greeter",
    "method": "SayHello",
    "deadline": 500,
    "pb_option": ["enum_as_name", "int64_as_string", "auto_default_values"]
  }
}
```

Surface parsed gRPC status details in the response body:

```json
{
  "grpc-transcode": {
    "proto_id": "1",
    "service": "helloworld.Greeter",
    "method": "SayHello",
    "show_status_in_body": true,
    "status_detail_type": "google.rpc.ErrorInfo"
  }
}
```

</details>

### `grpc-web`

The `grpc-web` Plugin is a proxy Plugin that can process [gRPC Web](https://github.com/grpc/grpc-web) requests from JavaScript clients to a gRPC service.

{% hint style="info" %}
**Priority** `505` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name                 | Type   | Required | Default                                | Description                                                                                             |
| -------------------- | ------ | -------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| cors\_allow\_headers | string | False    | "content-type,x-grpc-web,x-user-agent" | Headers in the request allowed when accessing a cross-origin resource. Use `,` to add multiple headers. |

**Sample configuration**

Enable gRPC-Web proxying with the default allowed headers:

```json
{
  "grpc-web": {}
}
```

Allow additional cross-origin request headers:

```json
{
  "grpc-web": {
    "cors_allow_headers": "content-type,x-grpc-web,x-user-agent,authorization"
  }
}
```

</details>

### `json-to-xml`

The `json-to-xml` Plugin dynamically converts upstream JSON responses to XML when the client includes `application/xml` in the request `Accept` header. The transformation is schema-free and template-free: JSON object keys map directly to XML element names.

{% hint style="info" %}
**Priority** `998` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name              | Type           | Required | Default               | Valid values | Description                                                                                                                       |
| ----------------- | -------------- | -------- | --------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| content\_types    | array\[string] | False    | \["application/json"] |              | Upstream Content-Type values eligible for JSON-to-XML conversion. Parameters like `; charset=utf-8` are stripped before matching. |
| root\_name        | string         | False    | `response`            |              | Name of the root XML element that wraps the entire response.                                                                      |
| array\_item\_name | string         | False    | `item`                |              | Tag name used for each child element when serializing JSON arrays.                                                                |
| enable\_decl      | boolean        | False    | true                  |              | When true, prepends the XML declaration (`<?xml version="1.0" encoding="UTF-8"?>`) to the output.                                 |
| pretty            | boolean        | False    | false                 |              | When true, the XML output is pretty-printed with indentation and newlines.                                                        |
| max\_body\_size   | integer        | False    | 1048576               | >=1          | Maximum response body size in bytes eligible for transformation. Bodies exceeding this limit pass through unchanged.              |
| ctx\_namespace    | string         | False    | `json_to_xml`         |              | Namespace under `ctx.helix` where plugin ctx fields are stored.                                                                   |

**Sample configuration**

Convert JSON responses to XML with the defaults:

```json
{
  "json-to-xml": {}
}
```

Customize the root element and pretty-print the output:

```json
{
  "json-to-xml": {
    "root_name": "order",
    "array_item_name": "line",
    "pretty": true
  }
}
```

Convert additional upstream content types and drop the XML declaration:

```json
{
  "json-to-xml": {
    "content_types": ["application/json", "application/hal+json"],
    "enable_decl": false,
    "max_body_size": 2097152
  }
}
```

</details>

### `jsonp-wrapper`

The `jsonp-wrapper` Plugin wraps a JSON response body as JSONP when the request includes a callback query argument (by default `callback`). The final payload is emitted as `callback(<json body>);` and the response `Content-Type` is set for JavaScript.

{% hint style="info" %}
**Priority** `850` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name                      | Type           | Required | Default                                 | Description                                                                  |
| ------------------------- | -------------- | -------- | --------------------------------------- | ---------------------------------------------------------------------------- |
| callback\_arg             | string         | False    | `callback`                              | Name of the query argument that carries the callback function name.          |
| validate\_callback        | boolean        | False    | true                                    | Validate the callback name against `callback_pattern`.                       |
| callback\_pattern         | string         | False    | `^[A-Za-z_$][0-9A-Za-z_$\.]*$`          | Pattern a callback name must match when validation is enabled.               |
| reject\_invalid\_callback | boolean        | False    | true                                    | Reject the request when the callback name fails validation.                  |
| append\_semicolon         | boolean        | False    | true                                    | Append a trailing semicolon to the JSONP payload.                            |
| strip\_callback\_arg      | boolean        | False    | true                                    | Remove the callback argument from the query string before proxying upstream. |
| extra\_strip\_args        | array\[string] | False    |                                         | Additional query arguments to strip before proxying upstream.                |
| skip\_methods             | array\[string] | False    | \["OPTIONS"]                            | HTTP methods that bypass JSONP wrapping.                                     |
| content\_type             | string         | False    | `application/javascript; charset=utf-8` | `Content-Type` set on the wrapped response.                                  |
| ctx\_namespace            | string         | False    | `jsonp_wrapper`                         | Namespace under `ctx.helix` where plugin ctx fields are stored.              |

**Sample configuration**

Wrap JSON responses as JSONP using the default `callback` argument:

```json
{
  "jsonp-wrapper": {}
}
```

Use a custom callback argument and strip extra query arguments before proxying:

```json
{
  "jsonp-wrapper": {
    "callback_arg": "jsonp",
    "strip_callback_arg": true,
    "extra_strip_args": ["_", "cachebust"]
  }
}
```

Relax callback validation and set a custom content type:

```json
{
  "jsonp-wrapper": {
    "validate_callback": false,
    "reject_invalid_callback": false,
    "content_type": "text/javascript; charset=utf-8"
  }
}
```

</details>

### `mocking`

The `mocking` Plugin is used for mocking an API. When executed, it returns random mock data in the format specified and the request is not forwarded to the Upstream.

{% hint style="info" %}
**Priority** `10900` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name               | Type    | Required | Default          | Description                                                                             |
| ------------------ | ------- | -------- | ---------------- | --------------------------------------------------------------------------------------- |
| delay              | integer | False    |                  | Response delay in seconds.                                                              |
| response\_status   | integer | False    | 200              | HTTP status code of the response.                                                       |
| content\_type      | string  | False    | application/json | Header `Content-Type` of the response.                                                  |
| response\_example  | string  | False    |                  | Body of the response, support use variables, like `$remote_addr $consumer_name`.        |
| response\_schema   | object  | False    |                  | The JSON schema object for the response. Works when `response_example` is unspecified.  |
| with\_mock\_header | boolean | False    | true             | When set to `true`, adds a response header `x-mock-by: Helix/{version}`.                |
| response\_headers  | object  | false    |                  | Headers to be added in the mocked response. Example: `{"X-Foo": "bar", "X-Few": "baz"}` |

**Sample configuration**

Return a fixed example body:

```json
{
  "mocking": {
    "content_type": "application/json",
    "response_example": "{\"status\":\"ok\",\"caller\":\"$remote_addr\"}"
  }
}
```

Generate random mock data from a JSON schema:

```json
{
  "mocking": {
    "response_schema": {
      "type": "object",
      "properties": {
        "id": { "type": "integer" },
        "name": { "type": "string" }
      }
    }
  }
}
```

Mock an error response with a delay and custom status:

```json
{
  "mocking": {
    "response_status": 404,
    "delay": 2,
    "response_example": "{\"error\":\"not found\"}",
    "response_headers": {
      "X-Mock-Scenario": "missing"
    }
  }
}
```

</details>

### `pgp-crypto`

The `pgp-crypto` Plugin handles OpenPGP message crypto at the gateway: it decrypts an encrypted value carried in the incoming request before proxying, and encrypts the upstream response before it reaches the client. Clients exchange base64-encoded OpenPGP ciphertext with the gateway while your upstream works in plaintext.

{% hint style="info" %}
**Priority** `898` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

**How it works**

Configure one or two operations. Each names a **direction** and a **channel**:

* **`decrypt`** reads OpenPGP ciphertext, base64-decodes it, and decrypts it with a private key.
* **`encrypt`** takes plaintext, encrypts it with a public key, and base64-encodes it.

Each operation sets `target` (`request` or `response`) and `source` (`body`, `header`, `query`, or `form`), so you can work in **either direction**: decrypt an inbound request value before it reaches your upstream, encrypt an outbound response value before it reaches the client, or any combination. Set `decrypt`, `encrypt`, or both. For a `body` source, `field` selects one JSON value; without `field` the whole body is the value. When you encrypt one body field, the client receives only that ciphertext; when you decrypt a request body without a field, the upstream receives the plaintext only.

**Configuration**

Set at least one of `decrypt` or `encrypt`.

| Name                 | Type    | Required                     | Default         | Description                                                                                                                                                        |
| -------------------- | ------- | ---------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| decrypt              | object  | One of `decrypt` / `encrypt` |                 | Decrypt an inbound or outbound value. See the `decrypt` table below.                                                                                               |
| encrypt              | object  | One of `decrypt` / `encrypt` |                 | Encrypt an inbound or outbound value. See the `encrypt` table below.                                                                                               |
| keys\_ctx\_namespace | string  | False                        | `key_value_map` | `ctx.helix` namespace where a dynamic key reference is looked up (populated by [`key-value-map`](/api-gateway/plugin-reference/plugins-general.md#key-value-map)). |
| max\_body\_size      | integer | False                        | `1048576`       | Maximum request/response body size in bytes.                                                                                                                       |
| ctx\_namespace       | string  | False                        | `pgp_crypto`    | Namespace under `ctx.helix` where plugin ctx fields are stored.                                                                                                    |

**`decrypt`**

| Name                 | Type    | Required    | Default      | Description                                                                                                                                              |
| -------------------- | ------- | ----------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| target               | string  | True        |              | `request` (decrypt an inbound value before upstream) or `response` (decrypt an outbound value before the client).                                        |
| source               | string  | True        |              | Where the ciphertext lives: `body`, `header`, `query`, or `form`. For `target: response`, only `body` or `header`.                                       |
| field                | string  | Conditional |              | Header, query, or form name, or a dotted JSON path in the body. Required for `header` / `query` / `form`; optional for `body` (absent = the whole body). |
| private\_key         | string  | True        |              | Armored OpenPGP private key, or a dynamic reference such as `$consumer.private_key` (see **Dynamic key references**). Stored encrypted at rest.          |
| passphrase           | string  | False       | `""`         | Private key passphrase. May be a dynamic reference. Stored encrypted at rest.                                                                            |
| fail\_policy         | string  | False       | `fail-close` | `fail-close` returns an error on failure; `fail-open` continues without the operation.                                                                   |
| fail\_close\_status  | integer | False       | `400`        | HTTP status when `fail-close` fails. `400` to `599`.                                                                                                     |
| fail\_close\_message | string  | False       |              | Error message when `fail-close` fails.                                                                                                                   |

**`encrypt`**

| Name                 | Type    | Required    | Default      | Description                                                                                                                                              |
| -------------------- | ------- | ----------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| target               | string  | True        |              | `request` (encrypt an inbound value before upstream) or `response` (encrypt an outbound value before the client).                                        |
| source               | string  | True        |              | Where the plaintext lives: `body`, `header`, `query`, or `form`. For `target: response`, only `body` or `header`.                                        |
| field                | string  | Conditional |              | Header, query, or form name, or a dotted JSON path in the body. Required for `header` / `query` / `form`; optional for `body` (absent = the whole body). |
| public\_key          | string  | True        |              | Armored OpenPGP public key, or a dynamic reference such as `$consumer.public_key`.                                                                       |
| fail\_policy         | string  | False       | `fail-close` | `fail-close` returns an error on failure; `fail-open` continues without the operation.                                                                   |
| fail\_close\_status  | integer | False       | `500`        | HTTP status when `fail-close` fails. `400` to `599`.                                                                                                     |
| fail\_close\_message | string  | False       |              | Error message when `fail-close` fails.                                                                                                                   |

**Dynamic key references**

Instead of inline armor, a key or passphrase may be a **reference** resolved per request from the request context, so each caller can use its own key. A reference is a single whole value that is one of:

* `$consumer.<field>` or `$user.<field>`: resolves to a per-caller key name (`<consumer name>.<field>`) fetched by [`key-value-map`](/api-gateway/plugin-reference/plugins-general.md#key-value-map) into `keys_ctx_namespace`, for example `$consumer.private_key`.
* `$ctx.helix.<path>`: resolves directly to the key material another plugin placed on the request context, for example `$ctx.helix.key_value_map.shared_key`.

For safety, key references are limited to those three forms: `$ctx.var.*` (caller-controlled data such as request headers or query args) is **rejected**, so a caller cannot inject its own key. Inline armor is used as-is; only a whole-value `$…` reference is treated as dynamic.

**Legacy configuration (`request` / `response`)**

The earlier `request` (decrypt) and `response` (encrypt) blocks, with top-level `private_key` / `public_key` / `passphrase`, keep working unchanged; legacy keys are always inline armor. New configurations should prefer `decrypt` / `encrypt`. The legacy tables:

**`request`** (decrypt an incoming value before proxying)

| Name                 | Type    | Required           | Default      | Description                                                                                                           |
| -------------------- | ------- | ------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------- |
| enabled              | boolean | False              | `true`       | Set `false` to turn request decryption off while keeping the configuration.                                           |
| body                 | object  | Exactly one source |              | Decrypt a value in the request body: `{ "field": "<dotted JSON path>" }`, for example `message` or `details.payload`. |
| header               | object  | Exactly one source |              | Decrypt a request header: `{ "field": "<header name>" }`.                                                             |
| query                | object  | Exactly one source |              | Decrypt a query parameter: `{ "field": "<parameter name>" }`.                                                         |
| form                 | object  | Exactly one source |              | Decrypt a form field: `{ "field": "<field name>" }`.                                                                  |
| fail\_policy         | string  | False              | `fail-close` | `fail-close` rejects the request when decryption fails; `fail-open` logs and forwards the request unchanged.          |
| fail\_close\_status  | integer | False              | `400`        | HTTP status returned when `fail-close` rejects the request. `400` to `599`.                                           |
| fail\_close\_message | string  | False              |              | Error message returned when `fail-close` rejects the request.                                                         |

Set exactly **one** of `body`, `header`, `query`, or `form`. The decrypted plaintext is written back to that same channel: a `body` source replaces the request body with the plaintext, and a `header`, `query`, or `form` source overwrites that same header, parameter, or field.

**`response`** (encrypt the upstream response before it reaches the client)

| Name                 | Type              | Required | Default      | Description                                                                                                                                                                                                                 |
| -------------------- | ----------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| enabled              | boolean           | False    | `true`       | Set `false` to turn response encryption off while keeping the configuration.                                                                                                                                                |
| body                 | boolean or object | False    | `true`       | `true` encrypts the whole response body. `{ "field": "<dotted JSON path>" }` encrypts only that field's value, and the client then receives only that ciphertext with no other fields. `false` leaves the body unencrypted. |
| fail\_policy         | string            | False    | `fail-close` | `fail-close` returns an error when encryption fails; `fail-open` returns the upstream response unencrypted.                                                                                                                 |
| fail\_close\_status  | integer           | False    | `500`        | HTTP status returned when `fail-close` fails. `400` to `599`.                                                                                                                                                               |
| fail\_close\_message | string            | False    |              | Error message returned when `fail-close` fails.                                                                                                                                                                             |

The response is encrypted to the body only. A response cannot carry the ciphertext in a header, query, or form: headers are sent before the body is available to encrypt, responses have no query string, and form encoding is a request-body format.

**Accepted key formats**

Paste the armored key in whichever form your key tooling produced. The Plugin normalizes it before use, so all of these are accepted:

| Form                                  | Example                                                     | Accepted |
| ------------------------------------- | ----------------------------------------------------------- | -------- |
| Full armor                            | `-----BEGIN PGP PRIVATE KEY BLOCK-----` … `-----END …-----` | yes      |
| Body only (no BEGIN/END)              | base64 key bytes / checksum only                            | yes      |
| JSON `\n` escapes                     | `"-----BEGIN...-----\\n\\nline\\n-----END...-----"`         | yes      |
| Whitespace-damaged or space-collapsed | UI joins that drop blank lines                              | yes      |

**Examples**

Decrypt an inbound request body field and encrypt the outbound response body, each with a per-caller key resolved from the consumer:

```json
{
  "pgp-crypto": {
    "decrypt": {
      "target": "request",
      "source": "body",
      "field": "message",
      "private_key": "$consumer.private_key",
      "passphrase": "$consumer.passphrase"
    },
    "encrypt": {
      "target": "response",
      "source": "body",
      "public_key": "$consumer.public_key"
    }
  }
}
```

Encrypt one request header before it reaches the upstream, with an inline key:

```json
{
  "pgp-crypto": {
    "encrypt": {
      "target": "request",
      "source": "header",
      "field": "x-signature",
      "public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
    }
  }
}
```

The legacy `request` / `response` configuration is still accepted. Request body field `message` (request-only):

```json
{
  "pgp-crypto": {
    "private_key": "-----BEGIN PGP PRIVATE KEY BLOCK-----\n...",
    "passphrase": "<your-passphrase>",
    "request": {
      "body": { "field": "message" },
      "fail_policy": "fail-close",
      "fail_close_status": 400
    }
  }
}
```

The client sends:

```json
{
  "keyName": "my-key",
  "message": "<base64(openpgp-message)>"
}
```

The upstream receives only:

```
<plaintext>
```

For `header` / `query` / `form` sources, the plaintext is written back to that same header, query parameter, or form field instead of replacing the whole body:

```json
{ "pgp-crypto": { "request": { "header": { "field": "encryption" } } } }
```

```json
{ "pgp-crypto": { "request": { "query": { "field": "encryption" } } } }
```

```json
{ "pgp-crypto": { "request": { "form": { "field": "encryption" } } } }
```

Response body (full upstream body encrypted and base64-encoded to the client):

```json
{
  "pgp-crypto": {
    "public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n...",
    "response": { "body": true }
  }
}
```

Response, one JSON field only:

```json
{
  "pgp-crypto": {
    "public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n...",
    "response": { "body": { "field": "message" } }
  }
}
```

With upstream `{"keep":1,"message":"hello"}`, the client body is **only** `<base64(PGP of hello)>` (`keep` is not returned).

Bidirectional:

```json
{
  "pgp-crypto": {
    "private_key": "...",
    "public_key": "...",
    "passphrase": "...",
    "request": { "body": { "field": "message" } },
    "response": { "body": true }
  }
}
```

**Supported keys**

RSA (2048, 3072, and 4096-bit) and ECC keys are supported for both encrypt and decrypt: Curve25519 (the modern default) and NIST P-256, P-384, and P-521. Legacy DSA/ElGamal and Brainpool keys are also accepted.

:::note

An ECC key pair typically pairs an EdDSA sign-only primary key with an ECDH encryption subkey. The Plugin uses the **ECDH subkey** for encryption; signing keys (ECDSA/EdDSA) are not used.

:::

A plugin key change takes effect on the next request, with no restart.

**Plugin order**

The default priority **898** sits just below `response-rewrite`, so any rewrite runs before crypto:

| Plugin             | Priority | Order                                |
| ------------------ | -------- | ------------------------------------ |
| `proxy-rewrite`    | 1008     | request rewrite first                |
| `response-rewrite` | 899      | response rewrite first               |
| `pgp-crypto`       | 898      | decrypt/encrypt after those rewrites |

</details>

### `proxy-rewrite`

The `proxy-rewrite` Plugin offers options to rewrite requests that Helix forwards to Upstream services. With this plugin, you can modify the HTTP methods, request destination Upstream addresses, request headers, and more.

{% hint style="info" %}
**Priority** `1008` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name                            | Type           | Required | Default | Valid values                                                                                                                            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------------------------- | -------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| uri                             | string         | False    |         |                                                                                                                                         | New Upstream URI path. Value supports gateway variables. For example, `$arg_name`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| method                          | string         | False    |         | \["GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS","MKCOL", "COPY", "MOVE", "PROPFIND", "PROPFIND","LOCK", "UNLOCK", "PATCH", "TRACE"] | HTTP method to rewrite requests to use.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| regex\_uri                      | array\[string] | False    |         |                                                                                                                                         | Regular expressions used to match the URI path from client requests and compose a new Upstream URI path. When both `uri` and `regex_uri` are configured, `uri` has a higher priority. The array should contain one or more **key-value pairs**, with the key being the regular expression to match URI against and value being the new Upstream URI path. For example, with `["^/app/(. *)/(. *)", "/$1-$2", ^/theothers/*", "/theothers"]`, if a request is originally sent to `/app/hello/world`, the Plugin will rewrite the Upstream URI path to `/app/hello-world`; if a request is originally sent to `/theothers/hello/world`, the Plugin will rewrite the Upstream URI path to `/theothers`. |
| host                            | string         | False    |         |                                                                                                                                         | Set [`Host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host) request header.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| headers                         | object         | False    |         |                                                                                                                                         | Header actions to be executed. Can be set to objects of action verbs `add`, `remove`, and/or `set`; or an object consisting of headers to be `set`. When multiple action verbs are configured, actions are executed in the order of `add`, `remove`, and `set`.                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| headers.add                     | object         | False    |         |                                                                                                                                         | Headers to append to requests. If a header already present in the request, the header value will be appended. Header value could be set to a constant, one or more gateway variables, or the matched result of `regex_uri` using variables such as `$1-$2-$3`.                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| headers.set                     | object         | False    |         |                                                                                                                                         | Headers to set to requests. If a header already present in the request, the header value will be overwritten. Header value could be set to a constant, one or more gateway variables, or the matched result of `regex_uri` using variables such as `$1-$2-$3`. Should not be used to set `Host`.                                                                                                                                                                                                                                                                                                                                                                                                     |
| headers.remove                  | array\[string] | False    |         |                                                                                                                                         | Headers to remove from requests.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| use\_real\_request\_uri\_unsafe | boolean        | False    | false   |                                                                                                                                         | If true, bypass URI normalization and allow for the full original request URI. Enabling this option is considered unsafe.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |

**Sample configuration**

Rewrite the upstream URI path and HTTP method:

```json
{
  "proxy-rewrite": {
    "uri": "/internal/v2/orders",
    "method": "POST"
  }
}
```

Rewrite the path with a regular expression:

```json
{
  "proxy-rewrite": {
    "regex_uri": ["^/api/(.*)/(.*)", "/$1-$2"]
  }
}
```

Add, set, and remove request headers and override the Host header:

```json
{
  "proxy-rewrite": {
    "host": "backend.internal",
    "headers": {
      "set": {
        "X-Api-Version": "2"
      },
      "add": {
        "X-Request-Source": "helix"
      },
      "remove": ["X-Debug-Token"]
    }
  }
}
```

</details>

### `response-rewrite`

The `response-rewrite` Plugin offers options to rewrite responses that Helix and its Upstream services return to clients. With the Plugin, you can modify HTTP status codes, request headers, response body, and more.

{% hint style="info" %}
**Priority** `899` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

For instance, you can use this Plugin to:

* Support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) by setting `Access-Control-Allow-*` headers.
* Indicate redirection by setting HTTP status codes and `Location` header.

:::tip

You can also use the redirect Plugin to set up redirects.

:::

| Name            | Type           | Required | Default | Valid values       | Description                                                                                                                                                                                                |
| --------------- | -------------- | -------- | ------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| status\_code    | integer        | False    |         | \[200, 598]        | New HTTP status code in the response. If unset, falls back to the original status code.                                                                                                                    |
| body            | string         | False    |         |                    | New response body. The `Content-Length` header would also be reset. Should not be configured with `filters`.                                                                                               |
| body\_base64    | boolean        | False    | false   |                    | If true, decode the response body configured in `body` before sending to client, which is useful for image and protobuf decoding. Note that this configuration cannot be used to decode Upstream response. |
| headers         | object         | False    |         |                    | Actions to be executed in the order of `add`, `remove`, and `set`.                                                                                                                                         |
| headers.add     | array\[string] | False    |         |                    | Headers to append to requests. If a header already present in the request, the header value will be appended. Header value could be set to a constant, or one or more gateway variables.                   |
| headers.set     | object         | False    |         |                    | Headers to set to requests. If a header already present in the request, the header value will be overwritten. Header value could be set to a constant, or one or moregateway variables.                    |
| headers.remove  | array\[string] | False    |         |                    | Headers to remove from requests.                                                                                                                                                                           |
| vars            | array\[array]  | False    |         |                    | An array of one or more matching conditions in the form of the gateway's expression syntax.                                                                                                                |
| filters         | array\[object] | False    |         |                    | List of filters that modify the response body by replacing one specified string with another. Should not be configured with `body`.                                                                        |
| filters.regex   | string         | True     |         |                    | RegEx pattern to match on the response body.                                                                                                                                                               |
| filters.scope   | string         | False    | "once"  | \["once","global"] | Scope of substitution. `once` substitutes the first matched instance and `global` substitutes globally.                                                                                                    |
| filters.replace | string         | True     |         |                    | Content to substitute with.                                                                                                                                                                                |
| filters.options | string         | False    | "jo"    |                    | RegEx options to control how the match operation should be performed. See the gateway's regex engine for the available options.                                                                            |

**Sample configuration**

Replace the response body and override the status code:

```json
{
  "response-rewrite": {
    "status_code": 200,
    "body": "{\"message\":\"rewritten by helix\"}"
  }
}
```

Set CORS headers on the response:

```json
{
  "response-rewrite": {
    "headers": {
      "set": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET, POST, OPTIONS"
      },
      "remove": ["Server"]
    }
  }
}
```

Substitute strings in the response body with filters:

```json
{
  "response-rewrite": {
    "filters": [
      {
        "regex": "http://",
        "scope": "global",
        "replace": "https://"
      }
    ]
  }
}
```

Rewrite the response only when a matching condition is met:

```json
{
  "response-rewrite": {
    "status_code": 403,
    "body": "{\"error\":\"forbidden\"}",
    "vars": [["status", "==", 401]]
  }
}
```

</details>

### `xml-to-json`

The `xml-to-json` Plugin provides optional bidirectional protocol translation between JSON and XML. By default it only transforms **responses**: when the client includes `application/json` in the `Accept` header, upstream XML is converted to JSON. When `transform_request` is enabled, eligible JSON request bodies are converted to XML before proxying.

{% hint style="info" %}
**Priority** `997` · **Category** Transformation
{% endhint %}

<details>

<summary>Configuration and details</summary>

| Name                    | Type           | Required | Default                         | Description                                                                                                                                                                                                                                     |
| ----------------------- | -------------- | -------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| transform\_response     | boolean        | False    | true                            | When true, convert eligible upstream XML responses to JSON when the client requests `application/json`.                                                                                                                                         |
| transform\_request      | boolean        | False    | false                           | When true, convert eligible JSON request bodies to XML before proxying to the upstream.                                                                                                                                                         |
| content\_types          | array\[string] | False    | \["application/xml","text/xml"] | Upstream Content-Type values eligible for XML-to-JSON conversion. Parameters like `; charset=utf-8` are stripped before matching.                                                                                                               |
| request\_content\_types | array\[string] | False    | \["application/json"]           | Client Content-Type values eligible for JSON-to-XML request conversion.                                                                                                                                                                         |
| root\_name              | string         | False    |                                 | Optional wrapper key for the JSON output. When set, the parsed document is nested under this key on responses and unwrapped from this key on requests. When omitted, the original XML root element name is preserved as the top-level JSON key. |
| request\_root\_name     | string         | False    | `request`                       | Root XML element used when the JSON request body is a primitive value, an array, or a multi-key object.                                                                                                                                         |
| array\_item\_name       | string         | False    | `item`                          | Tag name used for each child element when serializing root-level JSON arrays to XML.                                                                                                                                                            |
| enable\_decl            | boolean        | False    | true                            | When true, prepends the XML declaration to generated request XML.                                                                                                                                                                               |
| pretty                  | boolean        | False    | false                           | When true, JSON output is pretty-printed. When request transformation is enabled, XML request output is also pretty-printed.                                                                                                                    |
| max\_body\_size         | integer        | False    | 1048576                         | Maximum request or response body size in bytes eligible for transformation. Bodies exceeding this limit pass through unchanged.                                                                                                                 |
| ctx\_namespace          | string         | False    | `xml_to_json`                   | Namespace under `ctx.helix` where plugin ctx fields are stored.                                                                                                                                                                                 |

**Sample configuration**

Convert upstream XML responses to JSON with the defaults:

```json
{
  "xml-to-json": {}
}
```

Also convert JSON request bodies to XML before proxying:

```json
{
  "xml-to-json": {
    "transform_request": true,
    "transform_response": true,
    "request_root_name": "order"
  }
}
```

Wrap the JSON output under a root key and pretty-print it:

```json
{
  "xml-to-json": {
    "root_name": "data",
    "pretty": true,
    "content_types": ["application/xml", "text/xml", "application/soap+xml"]
  }
}
```

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.digitalapi.ai/api-gateway/plugin-reference/plugins-transformation.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
