Sigfox is a low-power wide-area network for sensors that send a few bytes a few times an hour. Sigfox devices send their messages to the Sigfox backend, which forwards them to Thinger.io with a callback. You can receive those callbacks with the Sigfox plugin, which creates a device and a Data Bucket for each new Sigfox device, or with one HTTP device per Sigfox device, which works on any account.
Message limits
Sigfox limits the size and the number of messages, so design the payload in bytes before you write the firmware:
| Direction | Payload | Messages per day |
|---|---|---|
| Uplink, device to network | Up to 12 bytes | Up to 140 |
| Downlink, network to device | Exactly 8 bytes | Up to 4 |
The device uses the 868 MHz band in Europe and the 902 MHz band in the United States. A downlink is only sent when the device asks for one with an uplink, through a BIDIR callback.
Choose how to integrate
| Sigfox plugin | HTTP device | |
|---|---|---|
| Account | A private instance: plugins are not available on the free Cloud account. | Any account, including the free Cloud account. |
| Devices | Creates an HTTP device and a Data Bucket for each new Sigfox device of a device type. | You create one HTTP device for each Sigfox device. |
| Payload | Decoded by the Sigfox backend with Custom payload config, or by your uplink code in the plugin. | Decoded by the Sigfox backend with Custom payload config. Payload code runs only in the plugin. |
| Downlinks | The downlink property of each device, encoded by your downlink code. | The Response Data property, already in the format Sigfox expects. |
Before you start
- Access to the Sigfox backend, with a device type that groups your devices.
- Your devices registered in the Sigfox backend with their ID and PAC. Program a Sigfox board shows how to read them from an Arduino MKR FOX 1200.
- Your server is
YOUR_SERVER:iot.thinger.ioon the free Cloud account, or your own domain on a private instance.
Connect with the plugin
Install the plugin
Go to Plugins > Marketplace and install Sigfox. Open it from the Plugins menu.
Add the device type
In Device Types, add a device type with the same name as the device type in the Sigfox backend, or use Default for all your devices.
Configure the callback behavior
In Callback Config, enable Auto provision resources and set:
Field What it does Device Identifier Prefix Prefix of the device ID; the Sigfox device ID is added after it, such as sigfox_1A2B3C.Bucket Identifier Prefix Prefix of the Data Bucket created for each device. Device Connection Timeout Minutes without messages before the device shows as disconnected. Assign Asset Type, Assign Asset Group, Assign Endpoint, Assign Project Optional. Classify the new devices and call an endpoint on each message. Initialize Downlink Data JSON that starts the downlinkproperty of each device. Set it if you use downlinks.To decode the raw Sigfox payload into fields, enable Callback Processing and write the
uplinkfunction (anddownlink, for downlinks) in Callback Code. Test it with Test Callback Code, then click Save.Create the callback in the Sigfox backend
Open Sigfox Integration in the plugin and copy the URL Pattern and the Authorization Header. In the Sigfox backend, create a custom callback for the device type with:
- Type DATA and UPLINK, or BIDIR to answer with downlinks.
- Channel URL, the copied URL pattern and method POST.
- Send duplicate off, so that a message received by several base stations is stored once.
- A Custom payload config if the backend decodes the payload (see Decode the payload).
- Header
Authorizationwith the copied value, and content typeapplication/json. - A JSON body with the fields you need, based on the Sample Body of the plugin, such as
{"temperature": {customData#temp}, "humidity": {customData#hum}, "data": "{data}"}.
On the next message of each Sigfox device, the plugin creates an HTTP device and its Data Bucket, and stores the message.
Connect without the plugin
Each Sigfox device is an HTTP device, and the Sigfox callback posts to its callback URL. The HTTP device stores the message in a Data Bucket, calls endpoints or sets properties like any other HTTP device, so you can build dashboards and alerts on it.
Deprecated
These steps use the HTTP callback of the device, which is deprecated. It keeps working, but for a new integration point the Sigfox callback to an API resource of a product instead: the same body works, the product can create each device on its first message with Auto Provision, and its response carries the downlink. Send the time as a ts field of the body ("ts": {time}000) instead of the ts query parameter.
Create the Data Bucket
Go to Data Buckets and create a Data Bucket, such as
sigfox_data, with From Device Write Call as Data Source. The callback only writes to Data Buckets that already exist.Create the HTTP device
Go to Devices, click Add Device and choose HTTP Device (Sigfox, Lora, cURL) as Device Type, with a Device Id that includes the Sigfox ID, such as
sigfox_1A2B3C. HereYOUR_DEVICEis that ID.Configure the device callback
Open the device, go to Callback and set Write Bucket to
sigfox_data, a Timeout longer than the time between messages, such as30minutes for a message every 10 minutes, and Response Data todownlinkif you use downlinks. Enable Authorization and copy the token from Overview: it isYOUR_TOKENbelow.From the API, set the same with
PUT /v3/users/{user}/devices/{device}/callback:json{ "actions": { "write_bucket": "sigfox_data", "send_property": "downlink" }, "properties": { "timeout": 30 } }Create the custom callback in the Sigfox backend
In the Sigfox backend, go to Device Type, open your device type, click Callbacks and create a new Custom callback.

Create a custom callback for the device type Fill in the form:
Field Value Type DATA and UPLINK. Use BIDIR to answer with downlinks. Channel URL Send duplicate Off. Otherwise a message received by several base stations is stored several times. Custom payload config How to split the payload into fields, such as temp::float:32:little-endian hum::float:32:little-endian. See Decode the payload.Url pattern https://YOUR_SERVER/v3/users/YOUR_USER/devices/YOUR_DEVICE/callback/data?ts={time}000Use HTTP Method POST Headers Authorizationwith the valueBearer YOUR_TOKEN, with a space afterBearer.Content type application/jsonBody The JSON to store, with Sigfox variables. See the example below. 
Type, channel, Send duplicate and Custom payload config {time}is the time of the message in seconds;ts={time}000turns it into the milliseconds the callback expects, so each record keeps the time the device sent it, not the time it arrived.A body that stores the decoded fields with the signal quality and the coarse location of the message:
json{ "device": "{device}", "temperature": {customData#temp}, "humidity": {customData#hum}, "data": "{data}", "seqNumber": {seqNumber}, "snr": {snr}, "rssi": {rssi}, "station": "{station}", "latitude": {lat}, "longitude": {lng} }The Sigfox backend replaces each variable before it posts the body:
{device}is the Sigfox ID,{data}the raw payload in hexadecimal,{seqNumber}the message counter,{snr},{rssi}and{station}the signal and the base station, and{lat}and{lng}a location with an accuracy of kilometers.{customData#temp}and{customData#hum}exist only if the Custom payload config definestempandhum. As the body haslatitudeandlongitude, each message also updates the device location.
A device type usually has several devices. To serve them all with one callback, create an HTTP device for each one, named after its Sigfox ID, and use {device} in the URL: https://YOUR_SERVER/v3/users/YOUR_USER/devices/sigfox_{device}/callback/data?ts={time}000. The token must then be an access token with the CallDeviceCallback action on those devices, because the token from Overview only works for its own device.
Write straight to a Data Bucket
If you only need the data, the callback can post to the Data Bucket instead of a device: https://YOUR_SERVER/v1/users/YOUR_USER/buckets/sigfox_data/data?ts={time}000, with an access token that only has the WriteBucket action on that Data Bucket (see POST /v1/users/{user}/buckets/{bucket}/data). Use {device} in the bucket ID for one Data Bucket per device. There is no device then: no connection state, no endpoints and no downlinks.
Decode the payload
The Custom payload config of the Sigfox callback splits the bytes of each message into named fields, which the body uses as {customData#name}. Fields are read in order from the first byte, separated by spaces, with the syntax name::type:bits:endianness:
| Type | Example | Reads |
|---|---|---|
uint | hum::uint:8 | An unsigned integer of the given number of bits. |
int | temp::int:16:little-endian | A signed integer of the given number of bits. |
float | temp::float:32:little-endian | A 32-bit or 64-bit floating-point number. |
The default byte order is big-endian. Microcontrollers such as the SAMD21 of the Arduino MKR boards and the ESP32 store numbers in little-endian, so add little-endian to every field of more than one byte that the firmware copies from memory.
The payload config must match the bytes the firmware sends, field by field. Send a C struct and mark it packed, so that the compiler adds no padding bytes between fields:
| Custom payload config | Struct in the firmware | Bytes |
|---|---|---|
temp::float:32:little-endian hum::float:32:little-endian | struct __attribute__((packed)) { float temp; float hum; } | 8 |
temp::int:16:little-endian hum::uint:8 | struct __attribute__((packed)) { int16_t temp; uint8_t hum; } | 3 |
Integers leave room for more fields within the 12 bytes. To keep decimals, send scaled values, such as the temperature in tenths of a degree (215 for 21.5 °C), and convert them in the plugin uplink code or in a product.
Program a Sigfox board
Any Sigfox module works with Thinger.io, as long as its payload matches the callback. This example uses the Arduino MKR FOX 1200, a SAMD21 board with an ATA8520 Sigfox module that runs on two AA or AAA batteries.

Prepare the Arduino IDE:
- In Boards Manager, install Arduino SAMD Boards.
- In Library Manager, install Arduino SigFox for MKRFox1200, Arduino Low Power and RTCZero, and SimpleDHT for the sensor of the second sketch.
- Select the board Arduino MKR FOX 1200 and its port.
Read the ID and PAC
Upload this sketch and open the serial monitor at 9600 baud. Register the board in the Sigfox backend at backend.sigfox.com/activate with the ID and the PAC it prints, or follow Arduino's first configuration tutorial.
#include <SigFox.h>
void setup() {
Serial.begin(9600);
while (!Serial) {}
if (!SigFox.begin()) {
Serial.println("Sigfox module not found");
return;
}
Serial.println("Firmware version: " + SigFox.SigVersion());
Serial.println("ID: " + SigFox.ID());
Serial.println("PAC: " + SigFox.PAC());
Serial.print("Module temperature: ");
Serial.println(SigFox.internalTemperature());
SigFox.end(); // put the module to sleep
}
void loop() {
}Send temperature and humidity
This sketch reads a DHT11 sensor on pin 0 and sends the temperature and the humidity every 11 minutes, within the 140 messages a day, as two floats that match temp::float:32:little-endian hum::float:32:little-endian. The built-in LED blinks once when the message is sent and three times when it fails. Without a DHT11, send SigFox.internalTemperature() as the temperature.
#include <SigFox.h>
#include <ArduinoLowPower.h>
#include <SimpleDHT.h>
#define DHT11_PIN 0
#define SEND_INTERVAL (11UL * 60UL * 1000UL) // 11 minutes: 131 messages a day
SimpleDHT11 dht11(DHT11_PIN);
// must match the Custom payload config of the callback
struct __attribute__((packed)) Reading {
float temp;
float hum;
};
void blink(int times, unsigned long ms) {
for (int i = 0; i < times; i++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(ms);
digitalWrite(LED_BUILTIN, LOW);
delay(ms);
}
}
void sendReading() {
float temp = 0, hum = 0;
if (dht11.read2(&temp, &hum, NULL) != SimpleDHTErrSuccess) {
Serial.println("DHT11 read failed");
return;
}
Reading reading = {temp, hum};
SigFox.begin();
delay(100);
SigFox.status(); // clear pending interrupts
delay(1);
SigFox.beginPacket();
SigFox.write((const uint8_t*)&reading, sizeof(reading)); // 8 bytes
int error = SigFox.endPacket();
SigFox.end();
if (error) {
Serial.println("Transmission failed");
blink(3, 500);
} else {
Serial.println("Transmission ok");
blink(1, 1000);
}
}
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
sendReading();
delay(SEND_INTERVAL);
// on batteries, sleep instead of waiting:
// LowPower.sleep(SEND_INTERVAL);
}Sleep mode and uploads
On batteries, replace delay() with LowPower.sleep() and remove the serial output. While the board sleeps, the IDE cannot upload a new sketch: press the reset button twice to enter the bootloader, then upload.
Result
In the Sigfox backend, open the device and go to Messages. Each message shows its payload in hexadecimal, the link quality and an icon with the result of the callback.

Click the callback icon to see the answer of Thinger.io: 200 - OK when the message was accepted.

In Thinger.io, the device shows as connected in Devices, and its Data Bucket stores one record per message with the fields of the callback body. Plot them in a dashboard with the Data Bucket as the source.

Downlinks
With a BIDIR callback, Thinger.io answers each message with downlink data, and the Sigfox network delivers its 8 bytes to the device.
- With the plugin: the plugin answers with the
downlinkproperty of the device, converted by yourdownlinkfunction. - Without the plugin: the HTTP device answers with its Response Data property, so the property must hold the answer that Sigfox expects: the Sigfox ID of the device as key, and
downlinkDatawith 16 hexadecimal characters.
Without the plugin, set the property like this, where YOUR_TOKEN is an access token of your account and 1A2B3C the Sigfox ID:
curl -X PUT "https://YOUR_SERVER/v3/users/YOUR_USER/devices/YOUR_DEVICE/properties/downlink" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"1A2B3C": {"downlinkData": "0102030405060708"}}}'Change the property from the console, a dashboard or the API, and the device receives the new value the next time it asks for a downlink. On the MKR FOX 1200, ask for it with SigFox.endPacket(true) and read the 8 bytes with SigFox.parsePacket() and SigFox.read(). The device can only receive 4 downlinks a day.