Transforms API
Manage user-defined Starlark transforms that reshape collected telemetry before it reaches the output pipeline in Prelude Collector.
Transforms are user-defined Starlark functions that the Prelude Collector runs against parsed telemetry before it reaches the output pipeline. After this first mention, the rest of this page refers to Prelude Collector as "the collector". A transform is the right place to derive a rate from a counter, normalize a vendor quirk, or drop entries you do not want shipped. This page covers when to use the API, common mistakes, and a worked example. For the full endpoint schema, see the API reference.
When to use this API
- Reshaping a single value. Convert units, format an enum, truncate a string, derive a ratio from a paired field — anything the built-in transforms do not already cover.
- Normalising vendor differences. Squash per-NetOS quirks (capitalisation, encoding, string framing) so downstream dashboards see one shape.
- Iterating safely. Update an existing transform with
PUTto fix a bug; the registry hot-reloads and the next collection cycle picks up the new expression without a restart.
To remove a transform from a field's pipeline, delete the entry
from the field's transforms list in the relevant mapping. The
transform itself can stay registered; it only runs when a mapping
references it by name.
How transforms are invoked
A transform is a function that takes one scalar value and
returns one scalar value. The collector wraps your code in
def transform(value): and runs it once per field per parsed entry.
Transforms cannot mutate other fields, see neighbouring entries, or
drop a row — those are mapping-level concerns (field-transforms,
value-transforms, ignore-values) covered in the
Transforms overview.
Common pitfalls
- Wrong signature. The expression is a function body that
receives
valueand mustreturna value. It is not a row mutator. ReturningNoneis treated as failure: a warning is logged and the original value is kept. - Slow transforms. Starlark is sandboxed but not free. Execution is capped at 1,000,000 Starlark steps; there is no separate wall-clock timeout, so a CPU-bound loop runs to the step limit.
- Hyphens in names. The validator regex is
^[a-z][a-z0-9_]*$—bps_to_humanis valid,bps-to-humanis not. - Name conflicts return
400 Bad Requestwith the failing field reported in the form-error envelope, not409 Conflict. This applies both to duplicates of existing custom transforms and to collisions with a built-in transform name.
Worked example: derive Mbps from a bytes-per-second counter
export BASE="https://collector.example.com"
export TOKEN="<your-api-token>"
# 1. Create a transform that converts bytes/sec to Mbps and rounds to 2 decimals.
curl -sk -X POST "$BASE/api/v1/transforms" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "bytes_per_sec_to_mbps",
"description": "Convert byte/sec counter to Mbps with 2-decimal precision.",
"expression": "mbps = to_mbps(value * 8)\nreturn round(mbps, 2)"
}'
# → 201 Created with the saved transform object (id, name, description, expression).
# 2. Inspect the saved transform.
curl -sk "$BASE/api/v1/transforms/1" \
-H "Authorization: Bearer $TOKEN"
# 3. Iterate: update the expression. Hot-reload picks up the new logic on the next cycle.
curl -sk -X PUT "$BASE/api/v1/transforms/1" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"expression": "return round(to_mbps(value * 8), 1)"
}'
# 4. Attach the transform by adding its name to the field's transforms list inside
# the mapping (see Models API → mappings). Transforms apply in left-to-right order.
# 5. Verify the new field appears on the parsed entries.
curl -sk "$BASE/api/v1/snapshots/$DEVICE_ID" \
-H "Authorization: Bearer $TOKEN"
Bruno: 07 Transforms / Create transform, 07 Transforms / Get transform, 07 Transforms / Update transform, 06 Snapshots / Get device snapshot
UI equivalent: https://collector.example.com/transforms/ — the same form with a "Test against a sample value" button that compile-checks the expression and runs it without saving.
Reference
Full request/response shapes for every endpoint live in the
Bruno collection. Required body fields are name
and expression; description is optional. There is no enabled
or version field — disable a transform by removing its name from
the mapping that uses it, or delete it outright with DELETE.