Agent skill

Sigfox Webhooks Skill

Receive and authenticate Sigfox webhooks (device-type custom callbacks). Use when setting up a Sigfox callback endpoint, checking the secret header you add to the callback, accepting GET, POST or PUT requests, debugging 401s, or handling data uplink, bidir, service status and error callbacks.

Install this skill

npx skills add hookdeck/webhook-skills --skill sigfox-webhooks


Sigfox (the IoT network now operated by UnaBiz) pushes device messages to your server through custom callbacks configured on a device type. Callbacks are not signed. You add a secret header to the callback's Headers section, and your endpoint authenticates each request by comparing that header with the value you stored.

A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Serve the endpoint over HTTPS and rotate the credential if it leaks.

When to Use This Skill

  • How do I receive Sigfox webhooks (callbacks)?
  • How do I authenticate a Sigfox custom callback?
  • Does Sigfox sign callback requests? (No: use a secret header)
  • How do I handle GET, POST and PUT callbacks, including empty bodies?
  • How do I handle uplink, bidir, service status or error callbacks?
  • Why does my Sigfox callback endpoint return 401?

Verification (core)

const crypto = require('crypto');

function safeEqual(a, b) {
  const x = Buffer.from(String(a), 'utf8');
  const y = Buffer.from(String(b), 'utf8');
  return x.length === y.length && crypto.timingSafeEqual(x, y);
}

function verifySigfoxRequest(headers, secret = process.env.SIGFOX_WEBHOOK_SECRET,
                             name = process.env.SIGFOX_WEBHOOK_HEADER || 'Authorization') {
  if (!secret) return false;                       // never accept when unconfigured
  const received = headers[name.toLowerCase()];    // Node lowercases header names
  return typeof received === 'string' && received !== '' && safeEqual(received, secret);
}
  • The header name is yours to choose (SIGFOX_WEBHOOK_HEADER, default Authorization). Sigfox forbids standard headers except Authorization; any custom name works.
  • SIGFOX_WEBHOOK_SECRET is the exact, whole header value, including any Bearer prefix you typed. Compare the whole value; don't strip or parse a prefix.
  • Return 401 for a missing or mismatched header. Compare in constant time (check lengths, then crypto.timingSafeEqual; in Python hmac.compare_digest on bytes).
  • The request method is also yours to choose: accept GET, POST and PUT, and handle an empty body (a GET carries its variables in the query string).
  • Read the body raw (express.raw({ type: '*/*' })) and parse only after the credential check.
  • Answer within 10 seconds. After a timeout or error code, Sigfox retries 1, 2 and 4 minutes later, so deduplicate on device, seqNumber and time (seqNumber restarts after 4096 messages).

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Callback Types

The request does not say which callback type sent it; the variables in your URL or body template decide the shape. The examples infer the type from the fields present. Event callbacks are created under Event configuration, and carry eventType only if your template includes {eventType}.

Callback typeSent WhenTelltale variables
DATA UPLINKA message is received from a devicedevice, deviceTypeId, data, seqNumber, time
DATA BIDIRAn uplink requests a downlinkack, longPolling plus the uplink variables
DATA ADVANCEDMessage details arrive (~30 s delay)lqi, linkQuality, duplicates
SERVICE STATUSDevice battery/temperature is reported (deprecated)batt, temp, snr
SERVICE ACKNOWLEDGEThe network confirms a downlinkinfoCode, downlinkAck
SERVICE REPEATERA repeater reports statusrepeatedMsg, whitelistDevices
ERRORA device breaches its keep-alive delayseverity, info
EVENTA device lifecycle or subscription event occurseventType (for example OFF_CONTRACT), sourceId, severity

Environment Variables

SIGFOX_WEBHOOK_HEADER=Authorization                 # header you added to the callback
SIGFOX_WEBHOOK_SECRET=Bearer your-long-random-value # exact whole header value

Local Development

Sigfox can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

npx hookdeck-cli listen 3000 sigfox --path /webhooks/sigfox

Use the printed URL as the callback URL in the Sigfox backend.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →