Guide

Transform Webhook Payloads

Transformations adapt an accepted source request before FastHook delivers it to a destination. They are saved JavaScript snippets, and a connection attaches them with a transform rule that references transformation_id.

Use transformations when the webhook should still be delivered, but the destination needs different headers, body, query, path, or method.

The original source request remains available for audit and replay. The transformed branch gets its own event data and execution history.

Dashboard Shape

In the dashboard, open Connections, choose a route, and enable Transform inside Connection rules. The current editor selects a saved transformation from the dropdown and stores it as a transform rule.

The Editor button opens the saved transformation editor. The connection rule does not hold the script body directly in the dashboard flow; it points to the saved transformation id.

Transform is a connection rule. The dropdown value becomes transformation_id in the saved rules array.

What A Transformation Changes

A transformation receives a request envelope with headers, body, query, path, and method. Return the full envelope when you want the outbound delivery to keep existing values plus your changes.

FastHook uses the transformed envelope for that connection branch only. Other connections from the same source can deliver the original shape or use a different transformation.

  • headers: add destination-specific headers such as x-provider or x-tenant.
  • body: rename fields, flatten provider envelopes, or add derived fields.
  • query: replace or normalize outbound query parameters.
  • path: send the destination request to a stable receiver path.
  • method: preserve POST unless a receiver contract requires another allowed method.
The source request remains evidence. The destination branch receives the transformed request envelope.

Write The Script

The dashboard transformation editor uses addHandler("transform", ...). The handler receives the current request envelope and a context object. Environment values are exposed through context.env and process.env.

Keep scripts deterministic and focused on request shaping. Use destination-specific constants or secrets from the transformation env object instead of hardcoding them in the script.

Transformation code
addHandler("transform", (request, context) => {
  const body = request.body ?? {};

  return {
    ...request,
    headers: {
      ...request.headers,
      "x-provider": "stripe",
      "x-api-key": context.env.API_KEY
    },
    body: {
      provider: "stripe",
      event_id: body.id,
      event_type: body.type,
      object_id: body.data?.object?.id,
      payload: body.data?.object ?? body
    },
    path: "/webhooks/provider-events"
  };
});

Create And Test Before Attaching

Create the saved transformation with POST /v1/transformations or upsert by name with PUT /v1/transformations. Then run the code against a realistic request sample before attaching it to production traffic.

For route-specific testing, call GET /v1/connections/:id/latest-input after sending a sample webhook. Use that envelope as the request body for PUT /v1/transformations/run.

Run a saved transformation against latest-input or a sample request before enabling it on the connection.
Create and run
# Create or update the reusable transformation.
curl -X PUT "https://api.fasthook.io/v1/transformations" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx" \
  -H "content-type: application/json" \
  -d '{
    "name": "add-custom-header",
    "code": "addHandler(\"transform\", (request) => ({ ...request, headers: { ...request.headers, \"x-provider\": \"stripe\" } }));",
    "env": {}
  }'

# Pull the latest input observed by the connection source.
curl "https://api.fasthook.io/v1/connections/web_orders/latest-input" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx"

# Test the saved transformation against a sample envelope.
curl -X PUT "https://api.fasthook.io/v1/transformations/run" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx" \
  -H "content-type: application/json" \
  -d '{
    "transformation_id": "trs_add_custom_header",
    "request": {
      "method": "POST",
      "path": "/orders",
      "headers": { "content-type": "application/json" },
      "query": {},
      "body": { "type": "order.created" }
    }
  }'

Attach To A Connection

Attach the tested transformation to the connection whose destination needs the changed payload. In API payloads, use type transform and transformation_id.

FastHook also accepts the legacy type transformation and normalizes it, but new examples should use transform because that is the stored dashboard shape.

  • A connection may contain more than one transform rule.
  • Rules are normalized in the order deduplicate, transform, filter, delay, retry.
  • If a filter also exists, make sure it matches the envelope after the transform rule has run, or preserve the original fields it needs.
  • When replacing the rules array, keep any existing filter, retry, delay, or deduplication rules that should remain active.
Patch connection
curl -X PATCH "https://api.fasthook.io/v1/connections/web_orders" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx" \
  -H "content-type: application/json" \
  -d '{
    "rules": [
      {
        "type": "transform",
        "transformation_id": "trs_add_custom_header"
      },
      {
        "type": "retry",
        "strategy": "exponential",
        "interval": 1000,
        "count": 5,
        "response_status_codes": ["429", "500-599"]
      }
    ]
  }'

Use Cases

  • Add an x-provider, x-team, or x-route header expected by the receiver.
  • Normalize Stripe, Shopify, GitHub, or custom provider envelopes into one internal event shape.
  • Flatten nested provider payloads for a simple destination service.
  • Move provider-specific fields into a payload object while keeping event_id and event_type stable.
  • Remove noisy fields that downstream consumers should not depend on.
  • Send one destination branch to /webhooks/provider-events while another branch keeps the original path.

Inspect And Debug

Live transformation execution creates an execution record. It links the event, connection id, transformation id, original event data id, transformed event data id, log level, logs, and timestamps.

If transformation code throws or the referenced transformation is missing, FastHook fails that event branch before destination delivery. Check the event failure message and transformation executions before looking at receiver logs.

Transformation failures happen before destination delivery, so debug from the event and execution record outward.
Inspection API
curl "https://api.fasthook.io/v1/events/evt_xxx" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx"

curl "https://api.fasthook.io/v1/transformations/trs_xxx/executions?limit=20&dir=desc" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx"

curl "https://api.fasthook.io/v1/transformations/trs_xxx/executions/txe_xxx" \
  -H "Authorization: Bearer fh_api_xxx" \
  -H "x-team-id: tm_xxx"

Safety Checklist

  • Preserve provider event ids and idempotency keys.
  • Preserve event type unless the destination contract explicitly renames it.
  • Handle missing optional fields with null-safe access.
  • Avoid depending on array order unless the provider documents it.
  • Keep secrets in transformation env values, not in code.
  • Prefer lowercase outbound header names for consistency.
  • Test with production-like payloads before enabling a route.

Next