← Back to documentation

Transformations and Payload Shaping

Use transform rules (mask, replace, delete, type-cast, regex) and output-level rendering to shape outgoing content.

10 min read

PayloadRelay lets you modify the incoming payload before it reaches your destinations. Transform rules run on every accepted request, after field validation but before delivery. Per-output rendering then adapts the transformed payload to each destination's format.

Purpose

Use this guide to:

  • Mask sensitive fields (e.g. credit card numbers, emails) before forwarding.
  • Replace field values with static or computed data.
  • Delete fields you don't want delivered.
  • Cast field types (string → number, string → boolean).
  • Apply regex substitutions on payloads.
  • Append new fields derived from request metadata.
  • Configure per-output rendering for email, Slack, and Discord targets.

Prerequisites and permissions

  • Endpoint edit access.
  • At least one attached relay target.

Transform rules

Transform rules modify the payload at the endpoint level — every output receives the transformed version. Rules are applied in order (use the up/down controls to reorder).

Field selector syntax

Each rule targets a specific field using a selector expression:

FormatSelector exampleResolves to
JSONuser.email, items.0.nameDot-path into the JSON body (numeric segments index arrays; bracket notation is not supported)
Form-encoded / query paramsemail, nameTop-level parsed field
XML/root/item/@id, //item/price/text(), $XPath expression, or $ for whole-body text transforms (literal substrings are used only if malformed XML falls back to plain text)
Plain text$, secret-token$ for the whole body, or a literal substring to match

Selectors are capped at 200 characters. Missing fields or missing intermediate paths are skipped without rejecting the request.

Rule types

Replace a field's value with masked characters, optionally randomizing the length for plausible deniability.

SettingDescription
Field selectorWhich field to mask
Mask characterCharacter used for masking (default *)
Randomize lengthWhen enabled, the masked value's length varies slightly so observers cannot infer the original value length

maskChar defaults to * and, when supplied, must be exactly one non-control character. With randomize length enabled, the output length is 1–8 characters; otherwise the mask length matches the field's string representation (so length information is intentionally preserved).

Example: Mask a credit card number.

MASK — Hide sensitive values
Field:    cardNumber
Type:     MASK
Char:     *
Result:   "****-****-****-1234" (last 4 preserved by using REPLACE on other rules)

REPLACE — Swap field values

Replace a field's current value with a new one.

SettingDescription
Field selectorWhich field to replace
Replace valueThe literal text to use
Value typeTEXT, NUMBER, BOOLEAN, or NULL — controls how the replacement is serialized in JSON

For JSON/form/query payloads, NUMBER replacements must parse as numbers and BOOLEAN replacements must be true or false; invalid values are rejected when saving the endpoint. NULL sets the field to JSON null rather than deleting it.

Examples:

  • Replace an environment tag: envproduction (type: TEXT)
  • Force a numeric field: priority1 (type: NUMBER)
  • Null out a field: debugInfo → null (type: NULL)

Remove a field from the payload entirely. The field will not appear in any delivery. For arrays, deleting a numeric selector such as items.1 removes that array element and shifts later entries left.

SettingDescription
Field selectorWhich field to delete

Example: Strip internal metadata before forwarding.

DELETE — Remove fields
Field:   _internalRequestId
Type:    DELETE

TYPE_CAST — Change the data type

Reinterpret a field's value as a different JSON type. Useful when upstream senders always emit strings but downstream receivers need typed values.

SettingDescription
Field selectorWhich field to cast
Cast toTarget type: TEXT, NUMBER, BOOLEAN, or NULL

Examples:

  • "42" (string) → 42 (number) — cast to NUMBER
  • "true" (string) → true (boolean) — cast to BOOLEAN
  • "hello" (string) → "hello" (string, no-op) — cast to TEXT

If the value cannot be parsed as the target type (for example "abc" → NUMBER), the field is set to null. TYPE_CAST is not available for XML or plain-text payloads.

Apply a regular-expression find-and-replace on the field's text value. Patterns are validated when saving. At delivery time, transforms use Java Pattern with MULTILINE and DOTALL, unanchored replaceAll semantics, $1/$2 back-references, and a 1-second safety timeout. If a regex replacement fails or times out, the original value is left unchanged.

SettingDescription
Field selectorWhich field to apply the regex to
Regex patternJava-compatible regular expression
ReplacementReplacement string (supports $1, $2 back-references)

Example: Normalize phone numbers by removing all non-digit characters.

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

Append fields

Append fields add new endpoint-level data after transform rules run. Every output receives the appended payload, so appended fields are available to webhook bodies, routing, outbound HMAC signing, Google Sheets, email rendering, and message templates.

SettingDescription
Field nameJSON/form/query dot path such as metadata.source or items.0.id; XML element name; ignored for plain text
ValueLiteral value to append
Value typeTEXT, NUMBER, BOOLEAN, or NULL for JSON/form/query payloads

Behavior and limits:

  • Up to 20 append rules per endpoint.
  • Field names are capped at 200 characters; values are capped at 65,536 characters.
  • JSON/form/query payloads require valid dot paths, reject duplicate append field names, and overwrite any existing payload field at that path.
  • NUMBER supports integers, decimals, and scientific notation; invalid numbers are rejected when saving. BOOLEAN must be true or false. NULL writes an actual JSON null and ignores the value field.
  • XML append rules add child elements to the root element (or raw text when the element name is blank). Plain-text rules append the literal value to the end of the body.
  • None payloads and bodyless GET/HEAD endpoints clear append rules. Query-parameter formats can use append rules because they produce a JSON payload.
  • JSON array/scalar roots cannot receive appended fields; use JSON object payloads when appending structured fields.

Per-output rendering

After transforms run, each output renders the payload for its destination type. These settings are configured per-output in the Target destinations tab.

Email outputs

Body formatDescription
RAW_PAYLOADPretty-printed raw JSON (or form/XML) payload
RAW_PAYLOAD_ROWSOne field per row with tab-indented nesting for nested objects and arrays. No custom template available for this format.
TEXTPlain text with an optional custom body template supporting template variables
HTMLHTML with an optional custom body template supporting template variables

For RAW_PAYLOAD_ROWS, XML payloads are rendered structurally, while text/plain payloads remain as raw text.

Slack outputs

  • Markdown toggle: When enabled, Slack mrkdwn formatting (*bold*, _italic_, <url|text>) is applied to the message body.

Discord outputs

  • TTS (Text-to-Speech) toggle: When enabled, messages are announced aloud via Discord's TTS feature. Use sparingly for urgent alerts.

Transform order and behavior

  1. Request arrives and passes inbound auth, rate limits, body filters, captcha validation, and field validation.
  2. Captcha fields configured for exclusion are stripped.
  3. Transform rules execute in the configured order (top to bottom); later rules see earlier results.
  4. Append field rules run after transforms and can overwrite fields transformed or removed earlier.
  5. The transformed/appended payload is used for routing, outbound HMAC signing, dispatch, and per-output rendering.
  6. Each output applies its own rendering format (email body, Slack markdown, etc.).

Accepted activity logs do not store request bodies, original or transformed. Delivery attempts use the transformed payload; when transforms are configured, the original raw body is not forwarded for failover replay.

Important: Transforms modify the payload once at the endpoint level. Per-output settings then only affect rendering — they cannot further transform individual fields. For destination-specific content, use message templates on Slack/Discord/Teams/Telegram outputs, or configure different routing filters per output to route different payloads to different targets.

Expected result and verification checks

  • Sensitive fields are masked or removed before delivery.
  • Field types match what downstream systems expect.
  • Email messages render in the chosen body format.
  • Slack and Discord messages respect markdown/TTS toggles.

Common issues and fixes

  • Transform not applied: Check the field selector — JSON paths use dot notation (user.email, items.0.name), XPath for XML, and $ or plain substring matching for text/plain.
  • CAST to NUMBER fails silently: If the value is non-numeric (e.g. "abc"), it becomes null. Use a REPLACE rule instead if you need a specific fallback value.
  • Email body looks wrong: Adjust the body format (RAW_PAYLOAD vs. TEXT with template) and preview before saving.
  • Slack formatting not rendering: Ensure markdown is toggled on for that output and use Slack mrkdwn syntax, not standard Markdown.
  • Regex back-references don't work: Use Java $1, $2 syntax (not Python \1, \2).

Related guides