Guides

Decode the payload

Turn raw bytes into named readings your rules and sinks can use.

A device sends 00018d460000300040e12402. Your billing system wants index: 18.061. A decoder is what happens in between.

Decoding is worth doing early: rules filter on decoded fields, and the custom output editor can only offer you fields it has seen a decoder produce.

Where a decoder lives

A decoder attaches to a device profile's technology, not to a device. Define it once for the model and every device on that profile decodes the same way.

Where: Profiles → Decoders to write one, then attach it on the profile's technology. Or Flow → Decoders, which is the same thing from the pipeline view.

Pick a kind

Grammar
Describe the payload's fields declaratively — offsets, widths, types, scaling. No code. The right default.
JavaScript
For payloads a grammar cannot express: conditional layouts, bit-packing that varies by message type, lookup tables.
None
Forward the payload untouched. Correct when the receiving system already understands the device's raw format.

Prefer grammar when it fits. It cannot loop forever, cannot throw in surprising ways, and reads as documentation of the payload format.

The two output groups

A decoder returns readings in two groups, and the distinction carries all the way to your sink:

{
  "telemetry": { "index": 18.061, "unit": "m3", "battery": 100 },
  "meta": { "seqNumber": 18, "rssi": -103.41 }
}

telemetry is what the device measured. meta is about the transmission. Put things in the right group — some output shapes deliver only one of them, and a reading filed under meta will go missing from a sink expecting telemetry.

The JavaScript sandbox

JavaScript decoders run in an isolated V8 sandbox:

  • No network, no filesystem. A decoder cannot call out.
  • Memory and time limited. A runaway script fails that one message; the pipeline keeps running.
  • TypeScript syntax is accepted; types are stripped before execution.
The sandbox can be switched off
If decoder execution is disabled for the deployment, JavaScript decoders are stored but not run, and payloads are forwarded raw. If a JS decoder appears to do nothing at all on an otherwise healthy pipeline, check this with whoever operates the Center.

When decoding fails

Decoding is best-effort. A decoder that throws, a profile with no decoder, or a callback carrying no payload at all does not fail the message — it continues with whatever it had.

That is deliberate: losing a reading is worse than delivering it undecoded. But it means a sink can see two different shapes for the same device.

Check payload.decodeError on a message that looks wrong. It is the difference between "the decoder is broken" and "this callback never had a payload to decode".

Not every Sigfox callback carries data
Only DATA_UPLINK, DATA_BIDIR and SERVICE_DATA_ADVANCED contain a payload frame. Status, acknowledge, repeater and error callbacks have nothing to decode and arrive as they came — that is not a decoder fault. Filter them out with a rule if your sink should not see them.

Decryption

If the profile defines encryption, the payload is decrypted before decoding, using the key on the device's binding. Keys are encrypted at rest and never shown again — leave the field blank when editing to keep the stored value.

Test it

Send a real uplink, then open Flow → Routing for a pair using that source. The preview renders with your decoder's actual field names and last-seen values, using the same code that performs delivery — so what you see is what a sink receives.

Until a decoder has run once there is nothing to preview, and the editor says so. Send one message and the fields appear.

Next

Choose what the sink receives
Now that you have fields, decide which of them are delivered.
Filter with rules
Act on decoded values — drop, route, or alert.
Copyright © 2026