Transformations and Payload Shaping
Use transform rules (mask, replace, delete, type-cast, regex) and output-level rendering to shape outgoing content.
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:
| Format | Selector example | Resolves to |
|---|---|---|
| JSON | user.email, items.0.name | Dot-path into the JSON body (numeric segments index arrays; bracket notation is not supported) |
| Form-encoded / query params | email, name | Top-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.
| Setting | Description |
|---|---|
| Field selector | Which field to mask |
| Mask character | Character used for masking (default *) |
| Randomize length | When 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.
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.
| Setting | Description |
|---|---|
| Field selector | Which field to replace |
| Replace value | The literal text to use |
| Value type | TEXT, 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:
env→production(type: TEXT) - Force a numeric field:
priority→1(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.
| Setting | Description |
|---|---|
| Field selector | Which field to delete |
Example: Strip internal metadata before forwarding.
Field: _internalRequestId
Type: DELETETYPE_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.
| Setting | Description |
|---|---|
| Field selector | Which field to cast |
| Cast to | Target 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.
| Setting | Description |
|---|---|
| Field selector | Which field to apply the regex to |
| Regex pattern | Java-compatible regular expression |
| Replacement | Replacement string (supports $1, $2 back-references) |
Example: Normalize phone numbers by removing all non-digit characters.
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.
| Setting | Description |
|---|---|
| Field name | JSON/form/query dot path such as metadata.source or items.0.id; XML element name; ignored for plain text |
| Value | Literal value to append |
| Value type | TEXT, 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.
NUMBERsupports integers, decimals, and scientific notation; invalid numbers are rejected when saving.BOOLEANmust betrueorfalse.NULLwrites 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.
Nonepayloads and bodylessGET/HEADendpoints 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 format | Description |
|---|---|
RAW_PAYLOAD | Pretty-printed raw JSON (or form/XML) payload |
RAW_PAYLOAD_ROWS | One field per row with tab-indented nesting for nested objects and arrays. No custom template available for this format. |
TEXT | Plain text with an optional custom body template supporting template variables |
HTML | HTML 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
- Request arrives and passes inbound auth, rate limits, body filters, captcha validation, and field validation.
- Captcha fields configured for exclusion are stripped.
- Transform rules execute in the configured order (top to bottom); later rules see earlier results.
- Append field rules run after transforms and can overwrite fields transformed or removed earlier.
- The transformed/appended payload is used for routing, outbound HMAC signing, dispatch, and per-output rendering.
- 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 fortext/plain. - CAST to NUMBER fails silently: If the value is non-numeric (e.g.
"abc"), it becomesnull. Use a REPLACE rule instead if you need a specific fallback value. - Email body looks wrong: Adjust the body format (
RAW_PAYLOADvs.TEXTwith 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,$2syntax (not Python\1,\2).
Related guides
- Endpoint Management
- HTTP Methods and Payload Formats
- Routing Filters — route different payloads to different targets.
- Message Templates — rich channel-native formatting for Slack, Discord, Teams, Telegram.
- Observability
- Troubleshooting