Skip to content
HookDeploy
On this page

Transformations

Reshape webhook payloads before forwarding with the visual editor, 35 built-in formatters, conditions, and form-encoded support.

HookDeploy’s payload transformation editor lets you modify webhook payloads before they reach each forward destination. No code, no JSONata — configure rules in the dashboard and preview against a captured request.

Configure transformations per destination in Endpoint → Settings → Forward destinations → Configure transform.

To reshape what HookDeploy stores (without changing what destinations receive), see Privacy filters.

Transformation modes

PassthroughAllowlist
Default behaviorForward all fieldsForward only selected fields
Best forTweaking a few fields on an otherwise complete payloadStrict PII control, analytics pipelines
Unselected fieldsIncluded unchangedDropped from output
Strip PIIToggle “Strip field” on specific pathsUncheck fields you don’t want
Static fieldsAdded on top of forwarded payloadAdded to allowlisted output

When to use passthrough

Your webhook sender sends a rich payload and you only need to rename amount to amount_usd, mask an email, or add a source field. Most fields pass through untouched.

When to use allowlist

You’re forwarding to a third-party analytics tool and want to send only event_type and amount — nothing else. Allowlist mode ensures no accidental PII leakage.

Field path syntax

Paths use dot notation to reach nested fields:

amount                    → top-level field
data.amount               → nested object
customer.email            → deeply nested
items.0.price             → first array element's price field

When you add a field rule, the output path defaults to the full source path (for example data.amountdata.amount). Change Rename to only when you want a different key. Nested allowlist selections keep their nested structure in the output.

In the visual editor, click any field in the JSON tree to add a transform rule. The editor loads your latest captured webhook as sample data so you can preview changes live.

Supported body formats

Transformations run on:

  • JSON bodies (application/json and JSON-parsed payloads)
  • Form-encoded bodies (application/x-www-form-urlencoded)

Form fields are parsed with standard URL form rules: a key that appears once stays a string; a key that appears more than once becomes an array.

a=1&b=2&b=3  →  { "a": "1", "b": ["2", "3"] }

Other content types are not transformed; the original body is forwarded unchanged.

Per-destination output format

Each forward destination has an output format:

SettingBehaviour
JSON (default)Encode the transformed object as application/json
Keep original formatWhen the inbound body was form-urlencoded, re-encode a flat result as form-urlencoded

If you choose Keep original format but the transformed object has nested objects (or other values form encoding cannot represent), HookDeploy falls back to JSON and records an error on the forward result. The transformed data is still delivered — it is not discarded.

Source × output matrix

Inbound Content-TypeOutput formatWhat is forwarded
JSONJSON or Keep originalTransformed JSON
application/x-www-form-urlencodedJSONTransformed object as JSON
application/x-www-form-urlencodedKeep originalForm re-encode when the result is flat; otherwise JSON fallback + recorded error
Other / emptyeitherTransform skipped; original body forwarded

Conditions

Any field or combine rule can include an Only when… condition. If the condition fails and you have not configured Otherwise… formatters, the rule is skipped (no write to the target path). If Otherwise… is configured, those formatters run instead.

Condition fields

FieldDescription
PathDefaults to the rule’s own source path (or the first combine source). You can point it at any other field.
OperatorSee table below
ValueCompared value — hidden for exists and is_empty

Operators

OperatorPasses when
equals / not_equalsValues compare equal / not equal under the coercion rules below
containsLeft is a string containing String(right), or an array with an element that equals right
starts_with / ends_withLeft string starts/ends with String(right)
greater_than / less_thanBoth sides coerce to finite numbers and compare
existsPath is present (not undefined)
is_emptyValue is null, undefined, '', or an empty array/object
matchesLeft string matches a bounded regex pattern

Type coercion

These rules matter when your condition value is typed differently from the payload field:

  • equals / not_equals: Same-type values use strict equality. A number and a numeric string can match (42 equals "42"). A boolean can match the lowercase strings "true" / "false". Other mixed types do not match via string conversion — for example 42 does not equal "42px", and an object never equals a string.
  • greater_than / less_than: Both sides run through Number(). If either side is NaN, the comparison fails (does not pass).
  • contains: Arrays use the same equality rules as equals for each element. Non-string, non-array left values fail.

Combine rules

Use Combine fields to join multiple source paths into one output string, then optionally run formatters and conditions on the result.

FieldDescription
SourcesOne or more field paths (up to 20)
SeparatorInserted between values (default: empty string)
Target pathWhere the combined string is written
Formatters / conditionSame as a field rule

Missing sources contribute an empty segment and a warning in the transform result.

Error handling

Formatters run independently per rule. If a formatter fails, that rule keeps the value from before the failed formatter and continues with remaining formatters when applicable. Other rules still apply. Errors are listed on the forward result — the destination still receives the partially transformed payload (not the pre-transform original, unless the transform never ran).

Storage privacy filters behave differently: see Privacy filters.

Formatter reference

Formatters run in the order you add them. Chain multiple formatters on one field — for example, cents_to_currency then prefix with value "USD ".

There are 35 built-in formatters. Two older aliases (cents_to_dollars, dollars_to_cents) still run on existing configs but are superseded by the currency formatters below.

Numeric

FormatterOptionsInput exampleOutput exampleBad input
cents_to_currencycurrency (ISO 4217, e.g. USD)4200 + USD"42.00"Missing/unsupported currency or non-number → error; value kept
currency_to_centscurrency"42.00" + USD4200Same
cents_to_dollars— (deprecated; prefer cents_to_currency + USD)4200"$42.00"Non-number → error
dollars_to_cents— (deprecated; prefer currency_to_cents + USD)42.004200Non-number → error
multiplyvalue (number)100 × 1.5150Non-number → error
dividevalue (number)100 ÷ 425Non-number or divide-by-zero → error
rounddecimals (0–10)3.141593.14Non-number → error
abs-4242Non-number → error

Zero-decimal currencies (for example JPY) and three-decimal currencies (for example KWD) use the engine’s ISO minor-unit rules.

String

FormatterOptionsInput exampleOutput exampleBad input
uppercase"hello""HELLO"Coerced via String
lowercase"HELLO""hello"Coerced via String
titlecase"hello world""Hello World"Coerced via String
trim" hello ""hello"Coerced via String
prefixvalue (string)"123" + "order_""order_123"
suffixvalue (string)"123" + "-v2""123-v2"
truncatemax (length)"hello world" max 5"hello"
maskshow_chars, position (start/end)"john@example.com" show 3 start"joh*************"
hash"john@example.com"SHA-256 hex digestPreview shows a placeholder; digest is computed at forward/storage time
splitseparator (default ,)"a,b,c"["a","b","c"]Always splits String(input)
joinseparator (default "")["a","b"] + "-""a-b"Non-array → error
replacefind, replace_with, regex (bool)"aa" find ab"bb"Missing find, or invalid/unsafe regex → error. Regex patterns max 256 chars; subjects capped at 10,000 chars for matching
substringstart (≥0), optional end"abcdef" start 1 end 4"bcd"Invalid bounds → error
pad_startlength, pad_string (default " ")"7" length 3 pad "0""007"Missing/invalid length → error
pad_endlength, pad_string"7" length 3 pad "0""700"Same

Date / time

FormatterOptionsInput exampleOutput exampleBad input
unix_to_iso1716912060"2024-05-28T14:01:00.000Z"Non-numeric / invalid → error
unix_to_date1716912060"2024-05-28"Same
iso_to_unix"2024-05-28T14:01:00Z"1716912060Unparseable → error
format_datepattern1716912060 + YYYY-MM-DD"2024-05-28"Missing pattern or invalid date → error
to_timezonetimezone (IANA, e.g. America/New_York)ISO/unix + Europe/LondonLocal wall time YYYY-MM-DDTHH:mm:ss.SSS (no Z)Missing/invalid zone or date → error

format_date tokens (UTC): YYYY MM DD HH mm ss SSS. Example pattern: YYYY-MM-DD HH:mm:ss.

Numeric timestamps smaller than 1e12 are treated as Unix seconds; larger values as milliseconds.

Type conversion

FormatterOptionsInput exampleOutput exampleBad input
to_string42"42"
to_number"42.5"42.5Non-numeric → error
to_boolean"true"trueUnrecognised → error
parse_json"{\"a\":1}"{ "a": 1 }Invalid JSON → error
stringify_json{ "a": 1 }"{\"a\":1}"Unserializable → error
json_escapehello "x"Escaped string contentEscape failure → error

Value override

FormatterOptionsInput exampleOutput exampleBad input
set_valuevalue (any)anyyour configured value
set_nullanynull
defaultvalue (fallback)"" / null / missingyour fallbackMissing value → error; non-empty input is left unchanged

Limits

These bounds are enforced when you save a transformation and again when it runs:

LimitValue
Rules per transformation (field + combine)50
Formatters per list (formatters or else_formatters)20
Sources per combine rule20
Regex pattern length (matches / replace)256 characters
Regex subject length (matching only)10,000 characters

Static fields

Add key-value pairs that appear in every forwarded payload regardless of the source webhook. Useful for injecting tenant IDs, source labels, or schema version markers:

{
  "source": "hookdeploy",
  "tenant_id": "acme-corp"
}

Static fields appear in the live preview in green. They are merged into the transformed output after field rules are applied.

Common recipes

Strip PII before forwarding to analytics

Mode: Passthrough

  1. Load a sample webhook in the transform editor
  2. Click customer.email in the field tree
  3. Enable Strip field (PII) on the rule card
  4. Repeat for customer.phone, customer.name, etc.
  5. Save the transform

Your analytics destination receives the full event minus sensitive customer fields.

Convert Stripe amounts with currency

Mode: Passthrough

  1. Click data.object.amount (or your amount field path)
  2. Optionally rename the output path
  3. Add formatter: cents_to_currency with currency USD
  4. Preview shows "42.00" from input 4200

See also: Stripe webhooks.

Normalize timestamp formats

Mode: Passthrough

  1. Click your Unix timestamp field (e.g. created)
  2. Add formatter: unix_to_iso
  3. Output becomes ISO 8601 with milliseconds: "2024-05-28T14:01:00.000Z"

For date-only fields, use unix_to_date. For custom layouts, use format_date.

Mask an email only in live mode

Mode: Passthrough

  1. Add a rule on customer.email with formatter mask
  2. Open Only when…
  3. Path: livemode, operator: equals, value: true
  4. Without Otherwise…, test-mode emails pass through unchanged

Allowlist only the fields your server needs

Mode: Allowlist

  1. Switch mode to Allowlist
  2. Select the nested paths your server expects — for example event_type, data.id, data.amount
  3. Add formatters as needed (e.g. cents_to_currency on data.amount)
  4. Preview confirms selected fields keep their nested paths unless you rename them

Everything else — including unexpected PII — is dropped.

Transform Twilio or Slack form posts

  1. Capture a form-urlencoded webhook (Twilio, Slack slash commands, etc.)
  2. Configure the destination transform against the parsed fields
  3. Prefer JSON output unless the downstream still requires form encoding
  4. If you need form output, keep the transform flat (no nested objects)

Live preview

The editor runs your transformation against sample data in real time:

  • Unchanged fields — default text color
  • Transformed fields — orange/warning color
  • Static fields — green/success color
  • Removed fields — shown as comments in passthrough mode
  • Hash — preview shows a clear placeholder; the real SHA-256 runs at forward time

Load your latest captured request or paste custom JSON to test against Stripe, GitHub, Twilio, or any provider payload. Preview errors mirror production forwarding: failed formatters keep their input value and other rules still apply.

Plan availability

Payload transformation for forwarding is available on Starter and above. Forwarding transformations are not limited by formatter type — every formatter listed here is available once transformations are unlocked.

Privacy filters (storage) use a different plan gate. See Privacy filters.

Next steps