Helper libraries
Import curated helper libraries into Webhook Relay functions with require() — JMESPath queries, JSON Schema validation, CSV, XML, YAML, JWT, HMAC, Mustache templates, time and HTML utilities.
JavaScript and Lua functions can import a curated set of built-in helper libraries with require(). They cover the things webhook transformations need most — safely reading nested data, querying JSON, converting between formats, verifying signatures and rendering templates — without npm packages or dependency management.
const data = require("data")
const event = JSON.parse(r.body)
const city = data.get(event, "customer.address.city", "unknown")
The same module names and camelCase methods are available in both runtimes. Only the documented built-in modules can be imported — npm packages, filesystem modules and arbitrary host modules are not available.
Available modules
| Module | Methods |
|---|---|
data | get, set, pick, omit, renameKeys, deepMerge, flatten, unflatten, groupBy, keyBy, uniqBy, sortBy, chunk |
jmespath | search(expression, value) |
schema | validate(schema, value), assert(schema, value) |
url | parse, build, encodeQuery, decodeQuery, encodeForm |
csv | parse, stringify |
xml | parse, stringify |
yaml | parse, stringify |
id | uuid, ulid |
jwt | sign, verify, decode |
secure | equal, verifyHMAC, base64urlEncode, base64urlDecode |
template | render |
time | parseISO, formatISO, add, subtract, startOf, endOf |
html | sanitize, toText, toMarkdown |
The existing runtime modules (http, json, crypto, time, jwt, bigquery and mailgun, where supported) remain available and can also be imported with require(). The full generated API reference — every method with parameters, returns and tested examples in both languages — is available in the function editor's autocomplete and via the MCP server.
Reading and reshaping data
The data module works with dotted paths and zero-based array indexes, such as customer.address.city or items[0].sku. Methods that write (set, omit, deepMerge and the array helpers) return new values and never mutate the input:
const data = require("data")
const event = JSON.parse(r.body)
// pick only the fields the destination needs
const summary = data.pick(event, ["id", "type", "customer.email"])
// group line items by SKU
const bySku = data.groupBy(data.get(event, "items", []), "sku")
r.setBody(JSON.stringify({ summary: summary, by_sku: bySku }))
For more complex queries, jmespath.search evaluates a full JMESPath expression against the payload:
const jmespath = require("jmespath")
const event = JSON.parse(r.body)
// names of all completed orders over $100
const bigOrders = jmespath.search(
"orders[?status == 'completed' && total > `100`].name",
event
)
Validating payloads with JSON Schema
Reject malformed webhooks before they reach your destination. schema.validate returns a result you can inspect; schema.assert stops the function with an error when validation fails:
const schema = require("schema")
const event = JSON.parse(r.body)
const result = schema.validate({
type: "object",
required: ["id", "email"],
properties: {
id: { type: "string" },
email: { type: "string", format: "email" }
}
}, event)
if (!result.valid) {
r.setResponseStatus(400)
r.setResponseBody("invalid payload")
r.stopForwarding()
}
Converting between formats
The csv, xml and yaml modules convert to and from JSON-compatible values, so you can accept a webhook in one format and forward it in another:
const csv = require("csv")
// header-aware by default: returns an array of objects
const rows = csv.parse(r.body)
r.setBody(JSON.stringify({ rows: rows }))
r.setHeader("Content-Type", "application/json")
A few safety notes on the format helpers:
csv.stringifyprefixes spreadsheet-formula cells by default to prevent formula injection — only disable it (preventFormulaInjection: false) for trusted data.delimitersupports CSV or TSV, andheader: falseswitches to plain arrays.xml.parsereturns an explicit element tree withname,attributes,childrenandtext. External entity resolution is disabled.yaml.parseaccepts a single document and returns JSON-compatible values.
Verifying JWTs and signatures
The jwt and secure modules handle token and webhook-signature verification with pinned algorithms and constant-time comparison:
const jwt = require("jwt")
const secure = require("secure")
// verify a JWT from a header (HS256, HS384, HS512 supported)
const claims = jwt.verify(r.headers["X-Auth-Token"], cfg.get("jwt_secret"), {
algorithm: "HS256",
issuer: "my-app"
})
// or verify a provider HMAC signature, e.g. "sha256=<hex>"
const ok = secure.verifyHMAC("sha256", cfg.get("signing_secret"), r.body,
r.headers["X-Hub-Signature-256"], { prefix: "sha256=" })
if (!ok) {
r.setResponseStatus(401)
r.stopForwarding()
}
jwt.decode returns the header and claims with verified: false — it is for inspection only and must never be used for authorization. secure.verifyHMAC accepts hex, base64 and base64url signatures plus an optional provider prefix. See webhook signature verification for provider-specific walkthroughs.
Rendering templates
template.render renders Mustache templates — handy for building notification messages without string concatenation:
const template = require("template")
const event = JSON.parse(r.body)
const text = template.render(
"Deploy {{version}} to {{environment}} finished with status {{status}}",
event
)
r.setBody(JSON.stringify({ text: text }))
r.setHeader("Content-Type", "application/json")
{{name}} is HTML-escaped; triple braces ({{{name}}}) insert raw text. Partials are not available.
Time, IDs, URLs and HTML
time— portable date arithmetic:parseISOreturns Unix seconds, the other methods accept RFC3339 text or Unix seconds and return RFC3339 text. Timezone options use IANA names, for example{ timezone: "America/New_York" }. See working with time for the existing runtimetimemodule.id— generateuuid()or sortableulid()identifiers for correlation IDs and idempotency keys.url—parse/buildURLs andencodeQuery/decodeQuery/encodeFormquery strings and form bodies. See also URL-encoded data.html—sanitizeuntrusted HTML, or convert it withtoTextandtoMarkdown, useful when relaying emails as webhooks into chat tools.
Limits
Helper inputs and outputs must be JSON-compatible and are bounded to 1 MiB, 10,000 nested items and 64 levels of nesting. A helper error stops the function with the module and method named in the error message. HTTP calls made from functions follow the platform egress policy.
Using secrets in functions
API tokens and signing secrets used by your functions don't belong in source code. Store them as write-only secret service connections and import them by alias:
const apiToken = require("secret:api-token")
See the secrets documentation for the full setup and binding flow.
