← Back to documentation

Transformations and Payload Shaping

Use mask, replace, delete, type-cast, and regex rules to shape outgoing content.

14 min read

PayloadRelay can change the incoming payload before the delivery. A transform rule applies to each accepted request, after the field validation and before the delivery. The formatting for each destination then adapts the changed payload.

Purpose#

Use this guide to:

  • Mask a sensitive field, such as a credit card number and an email address, before the delivery.
  • Replace a field value with fixed data.
  • Remove a field that the delivery must not contain.
  • Change a field type, such as a string to a number, or a string to a boolean.
  • Apply a regular expression substitution to a payload.
  • Add fields with fixed values to the payload.
  • Configure the formatting and the field selection for an email, Slack, Discord, Microsoft Teams, Telegram, and Google Sheets target.

Before you start#

  • Make sure that you can edit the endpoint.
  • Attach one relay target as a minimum.

Transform rules#

A transform rule changes the payload on the endpoint. Every destination receives the changed version. PayloadRelay applies the rules in order. To change the order, use the up control and the down control.

Field selector syntax#

Each rule uses a selector expression to identify a specific field:

FormatSelector exampleResolves to
JSONuser.email, items.0.nameA dot path into the JSON body. A numeric segment gives the index in an array. PayloadRelay does not support the bracket notation.
Form-encoded / query paramsemail, nameA top-level parsed field
XML/root/item/@id, //item/price/text(), $A selector in the XPath subset for XML, or $ for a transform on the complete body text. PayloadRelay uses a literal substring only when malformed XML falls back to plain text.
Plain text$, secret-token$ for the complete body, or a literal substring to match

A selector has a maximum of 200 characters. PayloadRelay ignores a missing field and a missing intermediate path, and it does not reject the request.

A MASK, REGEX, or TYPE_CAST rule changes a value that is a string, a number, a boolean, or null. If the selected value is a JSON object or a JSON array, MASK and REGEX keep the value with no change. For TYPE_CAST on an object or an array, read the TYPE_CAST section.

XPath subset for XML#

An XML selector uses a small part of XPath. PayloadRelay evaluates this subset in a time that is linear in the size of the payload. The same subset applies to an XML transform selector, an XML field validation rule, and an XML captcha field name.

  • A selector is one path. | can join two or more paths.
  • A path can start with / or //. A / or a // separates two steps.
  • A step is an element name, *, @attribute, @*, text(), ., or ...
  • A step other than . and .. can have one or more conditions. A condition is a position such as [1] or [last()], or a test of the node. A position starts at 1.
  • A test uses ., @name, the name of a child element, text(), local-name(), or name(). The test can use this value alone, or compare it with a quoted value through = or !=. For example: [@name], [@name='value'], [name!='value'], or [.='value'].
  • A test can also be contains(value, 'text') or starts-with(value, 'text'). and, or, and not() join two tests.
  • A field validation rule and a captcha field name can also count the nodes, for example count(//item). A transform selector cannot use count(), because a transform changes nodes.
  • A condition cannot contain a path, for example [b/c='x'] or [.//x]. The subset does not include other functions, axes such as ancestor::, numeric comparisons, operators, or variables.

Examples are /order/id, //item/@id, //item/price/text(), //item[@type='total']/price, /order//sku | /order/id, and //item[last()].

If a selector is outside the subset, PayloadRelay rejects the configuration when you save it. The error message names the supported forms. An older configuration can contain a selector outside the subset. PayloadRelay does not run that selector at relay time. It rejects the request with HTTP 400, and Activity records Unsupported Selector (UNSUPPORTED_SELECTOR). Thus PayloadRelay does not send a value that the rule must mask, replace, or delete. The error reason names the rule type: a transform rule, a field validation rule, or the captcha field name. To correct the rule, change the selector and save the endpoint. An UNSUPPORTED_SELECTOR outcome does not count toward auto-pause.

An XML transform selector is a literal substring for the plain-text fallback if it contains no /, [, ], or @, does not start with ., and has no axis name before ::. For example, std::string and fe80::1 are literal substrings.

An XML payload can have a maximum of 100 nested element levels. A webhook request with more levels returns HTTP 400 and records MALFORMED_XML_PAYLOAD. For an email body with more levels, PayloadRelay applies the transforms as plain text.

Rule types#

MASK: hide sensitive values

Replace a field value with mask characters. You can change the mask length.

SettingDescription
Field selectorThe field to mask
Mask characterThe character used for masking. The default is *.
Randomize lengthChanges the mask length, and an observer cannot then calculate the length of the original value.

maskChar is * by default. If you give a value, it must be exactly one non-control character. An emoji, such as 🔒, is one character. With the randomize length control enabled, the mask is 1 to 8 characters. If the control is disabled, the mask length is the same as the string form of the field. So the length information stays.

MASK replaces the complete value. It cannot keep a part of the value.

Example: mask a credit card number.

Code Example
Field:    cardNumber
Type:     MASK
Char:     *
Input:    "4111-1111-1111-1234"
Result:   "*******************"

To keep the last 4 digits, use a REGEX rule instead of MASK:

Code Example
Field:       cardNumber
Type:        REGEX
Pattern:     \d(?=(?:\D*\d){4})
Replacement: *
Input:       "4111-1111-1111-1234"
Result:      "****-****-****-1234"

REPLACE: replace field values

Replace the current value of a field with a new value.

SettingDescription
Field selectorThe field to replace
Replace valueThe literal text to use
Value typeTEXT, NUMBER, BOOLEAN, or NULL. This controls the JSON value type.

For a JSON, form, or query payload, a NUMBER replacement must parse as a number. A BOOLEAN replacement must be true or false. PayloadRelay rejects an invalid value when you save the endpoint. NULL sets the field to JSON null, and it does not delete the field.

Examples:

  • Replace an environment tag: set env to production (type: TEXT).
  • Set a numeric field: set priority to 1 (type: NUMBER).
  • Set a field to null: set debugInfo to null (type: NULL).

Remove a field from the payload. The field does not appear in a delivery. For an array, a numeric selector such as items.1 removes that array element, and it moves the later entries to the left.

For an XML payload, an XPath attribute selector, such as //item/@secret, removes that attribute from its element.

SettingDescription
Field selectorThe field to remove

Example: remove internal metadata before forwarding.

DELETE: remove fields
Field:   _internalRequestId
Type:    DELETE

TYPE_CAST: change the data type

Change a field value to a different JSON type. Use this rule when the sender sends strings and the receiver needs typed values.

SettingDescription
Field selectorThe field to cast
Cast toThe target type: TEXT, NUMBER, BOOLEAN, or NULL

Examples:

  • Cast to NUMBER: "42" (string) becomes 42 (number).
  • Cast to BOOLEAN: "true" (string) becomes true (boolean).
  • Cast to TEXT: "hello" (string) stays "hello" (string, with no change).

If PayloadRelay cannot parse the value as the target type, for example, "abc" cast to NUMBER, it sets the field to null. TYPE_CAST is not available for an XML payload or a plain-text payload.

If the selected value is a JSON object or a JSON array:

  • A cast to TEXT writes the compact JSON text of the value as a string. For example, {"a": 1} becomes "{\"a\":1}".
  • A cast to NULL sets the field to null.
  • A cast to NUMBER or BOOLEAN keeps the value with no change.

Apply a regular expression find-and-replace operation to a text value of a field. A pattern uses the Java-compatible syntax. PayloadRelay examines the pattern and the replacement when you save them. At the delivery, the replacements apply to all the matches, with multiline behavior and dot-matches-newline behavior, the $1 and $2 group references, and a 1-second safety timeout. If a replacement fails or times out, PayloadRelay keeps the original value.

SettingDescription
Field selectorThe field for the regular expression
Regex patternA Java-compatible regular expression
ReplacementA replacement string that supports $1 and $2 back references

The replacement uses the Java replacement syntax:

  • $1, $2, and the other numbers refer to a capture group in the pattern. ${name} refers to a named group, such as (?<name>...). The group must exist in the pattern.
  • To write a literal $, write \$. To write a literal backslash, write \\.
  • A backslash at the end of the replacement is not valid.

If the replacement is not valid for the pattern, the API rejects the endpoint with a 400 error when you save it.

For a plain-text payload with a substring selector, the pattern runs on the selected substring. PayloadRelay then replaces every occurrence of that substring in the body with the result. With the $ selector, the pattern runs on the complete body.

Example: normalize phone numbers by removing non-digit characters.

REGEX: pattern-based substitution
Field:       phoneNumber
Pattern:     [^0-9]
Replacement: (empty)
Result:      "+15551234567" → "15551234567"

Append fields#

An append field adds endpoint data after the transform rules apply. Every destination receives the payload with the added fields. The added fields are available to a webhook body, the routing, the outbound HMAC signing, Google Sheets, the email formatting, and the message templates.

SettingDescription
Field nameA JSON, form, or query dot path such as metadata.source or items.0.id. For XML, use an element name. A plain-text payload ignores this field.
ValueThe literal value to add
Value typeTEXT, NUMBER, BOOLEAN, or NULL for a JSON, form, or query payload

Behavior and limits:

  • An endpoint can have a maximum of 20 append rules.
  • A field name has a maximum of 200 characters. A value has a maximum of 65,536 characters.
  • A JSON, form, and query payload needs a valid dot path. PayloadRelay rejects a duplicate append field name. An append rule writes over an existing field at that path.
  • NUMBER supports an integer, a decimal, and scientific notation. PayloadRelay rejects an invalid number when you save. BOOLEAN must be true or false. NULL writes an actual JSON null, and it ignores the value field.
  • An XML append rule adds a child element to the root element. It adds raw text when the element name is blank. If a DELETE rule removed the root element, PayloadRelay skips the XML append rules. A plain-text rule adds the literal value to the end of the body.
  • A numeric segment in a JSON field name selects an element of an existing array. For example, items.0.flag sets flag on the first element of items. If that element is a string, a number, a boolean, or null, PayloadRelay replaces it with an object that holds the new field. If the index is negative, is not in the array, or the segment is not a number, PayloadRelay skips the rule. An append rule never replaces an array with an object.
  • A None payload and a bodyless GET or HEAD endpoint clear the append rules. A query-parameter format can use the append rules, because it makes a JSON payload.
  • A JSON array root and a JSON scalar root cannot receive appended fields. To append structured fields, use a JSON object payload.

Size of the transformed payload#

The transform rules and the append rules cannot make the payload larger than the payload size limit of the plan. For example, a REPLACE rule with a one-character selector and a long value can make a payload many times larger. If the result is larger than the limit, PayloadRelay rejects the request with 413. Activity records PAYLOAD_TOO_LARGE, and PayloadRelay sends nothing to the destinations.


Per-output formatting#

After the transforms apply, each destination formats the payload for its destination type. Configure these values for each destination in the Outputs tab.

Email outputs#

Body formatDescription
RAW_PAYLOADThe raw JSON, form, or XML payload, with formatting. A text payload or an XML payload arrives as its text, without JSON quotes.
RAW_PAYLOAD_ROWSOne field in each row, with an indentation of two spaces for each level of the nested objects and arrays. This format has no custom template.
TEXTPlain text, with an optional custom body template that supports template variables
HTMLHTML, with an optional custom body template that supports template variables

For RAW_PAYLOAD_ROWS, an XML payload uses its own structure. A text/plain payload stays raw text.

An HTML custom template escapes each value that it inserts, also {{email.htmlBody}}. So a payload value cannot add markup to the email. PayloadRelay does not change the markup that you write in the template. It does not remove a javascript: link from that markup.

Slack outputs#

  • The Markdown control: when it is enabled, Slack applies the mrkdwn formatting, such as *bold* and _italic_. This applies to the Slack text that PayloadRelay creates and to the body sections of a message template. Slack-native text, blocks, or attachments from the sender keep their own text and their own formatting rules.
  • PayloadRelay escapes &, <, and > in the text that it creates from the payload. When the Markdown control is enabled, PayloadRelay also escapes these characters in each value that a template inserts. So a payload value cannot mention @channel or a user, and it cannot add a <url|text> link. A mention or a link works only in the literal text of the template.

Discord outputs#

  • The TTS (Text-to-Speech) control: when it is enabled, Discord speaks the messages. Use it for an urgent alert.
  • The control that cuts a long message: it is disabled by default. When it is disabled, Activity records Discord content above the Discord 2000-character limit as a failed delivery. When it is enabled, PayloadRelay makes the content shorter before it sends the message. A message template does not use this control. Refer to Rich Message Templates.
  • A message that PayloadRelay creates sets allowed_mentions to {"parse": []}. So a payload value cannot mention @everyone, a role, or a user. A payload in the Discord message shape keeps its own allowed_mentions.

Transform order and behavior#

  1. The request arrives and passes the inbound authentication, the rate limits, the body filters, the captcha test, and the field validation.
  2. PayloadRelay removes the captcha fields that the configuration excludes.
  3. PayloadRelay applies the transform rules in the configured order, from the top to the bottom. A later rule uses the earlier results.
  4. The append field rules apply after the transforms. They can write over a field that an earlier rule changed or removed.
  5. PayloadRelay uses the payload with the transforms and the appended fields for the routing, the outbound HMAC signing, the formatting for each destination, and the delivery.
  6. Each destination applies its own formatting, such as an email body or the Slack Markdown.

An empty plain-text body or an empty XML body stays empty after the transforms. It does not become the text null.

An accepted activity log does not store a request body, original or transformed. A delivery attempt uses the transformed payload. When the transforms are configured, PayloadRelay does not send the original raw body for a failover.

The transforms change the payload one time, on the endpoint. The values for each destination only format the result. They cannot change the individual fields again. For destination-specific content, use message templates on a Slack, Discord, Teams, or Telegram destination. You can also use routing filters for different destinations.

Expected result#

  • PayloadRelay masks or removes a sensitive field before the delivery.
  • The field types agree with the downstream systems.
  • An email message uses the selected body format.
  • A Slack message obeys Enable Markdown formatting. A Discord message obeys Send as text-to-speech (TTS). A generated Discord message also obeys Truncate long messages.

Common issues and fixes#

  • PayloadRelay does not apply the transform: make sure that the field selector is correct. A JSON path uses the dot notation (user.email, items.0.name), XML uses the XPath subset for XML, and text/plain uses $ or a plain substring match.
  • A CAST to NUMBER fails with no message: a value that is not a number, such as "abc", becomes null. If you need a fallback value, use a REPLACE rule.
  • The email body is wrong: change the body format (RAW_PAYLOAD, or TEXT with a template), and read the preview before you save.
  • The Slack formatting fails: turn on Markdown for the destination, and use the Slack mrkdwn syntax, not the standard Markdown.
  • The regex group references fail: use the Java syntax $1 and $2, and not the Python syntax \1 and \2.
  • The API rejects a REGEX rule with Regex transform replacement is invalid.: the replacement has an error. It refers to a group that the pattern does not have, or it has a $ or a backslash that is not escaped. Write \$ for a literal dollar sign.