Skip to content
Stand with Ukraine flag

IoT Rule Chain Contribution Guide

Welcome to ThingsBoard IoT Hub. This guide walks you through contributing a rule chain that end users can install on their ThingsBoard instance in a single click.

A rule chain contribution is a single JSON file that you export from ThingsBoard and upload through IoT Hub. When a user clicks Install on your rule chain, the platform imports it, and it is immediately available to link into their message flow.


Create a free Creator account on the ThingsBoard Creator Portal to start publishing your items.


When a user installs your rule chain from IoT Hub:

  1. The platform downloads your rule chain JSON file.
  2. The user chooses in the install dialog — import only, or also set the chain as the default rule chain for a device or asset profile (with an overwrite confirmation if the profile already has one).
  3. The rule chain appears in the user’s Rule Chains list and starts receiving messages as soon as it is wired in or bound to a profile.

A rule chain contribution is structurally self-contained — every node (its type, name, configuration) and every connection between nodes are encoded in the JSON. Chains built around external nodes are the exception: a chain that publishes to Kafka, an MQTT broker, or a REST endpoint only works once that external system is reachable and its address and credentials are set on the node. Document such prerequisites in the readme’s # Requirements section (see Readme content).

  1. In your ThingsBoard instance, build and test your rule chain — starting from the default root chain (see Start from the default root chain).
  2. Export it as a JSON file — see Export the rule chain.
  3. Go to IoT Hub → + Add new item. A four-step wizard walks you through the rest:
    • Upload — upload your exported JSON. The platform recognizes it as a rule chain automatically and pre-fills the Name.
    • Listing — fill in the listing metadata (see Fill in the listing). Name is pre-filled from the JSON, Icon and Color are filled automatically based on the rule chain type (you can change them), and Description contains a fill-in template. Categories, Use Cases, and the supported ThingsBoard version must be selected manually.
    • Readme — the editor is pre-filled with the recommended readme structure. Replace each placeholder section with your documentation (see Readme content).
    • Review & Submit — verify everything and submit the version for review.

The sections below are a complete reference for each step.

Author your rule chain as an extension of the default root rule chain, not as a bare standalone chain. Start from the root chain of a fresh tenant, then wire the nodes of your logic onto the relevant path — for example, a flow that forwards telemetry to an external system hangs off the Post telemetry route.

This matters because of how users install your chain. The install dialog lets them set your chain as a profile’s default rule chain — every message from that profile’s devices then flows through your chain instead of the root chain. If your chain doesn’t include the default processing (saving telemetry and attributes, handling RPC), devices bound to it silently stop persisting data. Extending the default root keeps the platform behavior intact and makes your logic purely additive.

Give the added logic a name — e.g. the Kafka-publishing flow — and reuse that name throughout your readme, so users can tell your nodes apart from the default ones at a glance.

  1. In your ThingsBoard instance, go to the Rule Chains page.
  2. Locate your rule chain in the list.
  3. Click the Export rule chain button (download icon) in the chain’s row.
  4. Save the resulting .json file — this is your upload file.

The exported file is a complete rule chain definition and requires no further editing to be valid.

A valid rule chain file is a JSON object with a ruleChain header and a metadata body containing nodes and connections. The ruleChain.root flag must be false — if you authored your logic directly in the tenant’s root chain, copy it into a regular chain and export that copy.

The example below is trimmed from a real export — the default root chain’s Message Type Switch and Save Timeseries nodes, extended with a two-node Kafka-publishing flow. A full export keeps the remaining default-processing nodes (attribute saving, RPC handling) as well.

{
"ruleChain": {
"name": "Send IoT Device Data to Kafka",
"type": "CORE",
"root": false,
"debugMode": false,
"additionalInfo": {
"description": "Forward device telemetry to a Kafka topic, enriching each record with device identity (deviceId, deviceName, deviceType) fetched from the originator."
}
},
"metadata": {
"version": 6,
"firstNodeIndex": 0,
"nodes": [
{
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
"name": "Message Type Switch",
"singletonMode": false,
"configurationVersion": 0,
"configuration": {
"version": 0
},
"additionalInfo": {
"layoutX": 347,
"layoutY": 149
}
},
{
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"singletonMode": false,
"configurationVersion": 1,
"configuration": {
"defaultTTL": 0,
"useServerTs": false,
"processingSettings": {
"type": "ON_EVERY_MESSAGE"
}
},
"additionalInfo": {
"layoutX": 824,
"layoutY": 156
}
},
{
"type": "org.thingsboard.rule.engine.metadata.TbGetOriginatorFieldsNode",
"name": "Fetch Device Info",
"debugSettings": {
"failuresEnabled": true,
"allEnabled": false,
"allEnabledUntil": 0
},
"singletonMode": false,
"configurationVersion": 1,
"configuration": {
"dataMapping": {
"id": "deviceId",
"name": "deviceName",
"type": "deviceType"
},
"ignoreNullStrings": false,
"fetchTo": "DATA"
},
"additionalInfo": {
"description": "",
"layoutX": 1129,
"layoutY": 156
}
},
{
"type": "org.thingsboard.rule.engine.kafka.TbKafkaNode",
"name": "Send to Kafka",
"debugSettings": {
"failuresEnabled": true,
"allEnabled": false,
"allEnabledUntil": 0
},
"singletonMode": false,
"configurationVersion": 1,
"configuration": {
"topicPattern": "iot.telemetry",
"keyPattern": "$[deviceId]",
"bootstrapServers": "localhost:9092",
"retries": 0,
"batchSize": 16384,
"linger": 0,
"bufferMemory": 33554432,
"acks": "-1",
"otherProperties": {},
"addMetadataKeyValuesAsKafkaHeaders": true,
"kafkaHeadersCharset": "UTF-8"
},
"additionalInfo": {
"description": "",
"layoutX": 1461,
"layoutY": 157
}
}
],
"connections": [
{
"fromIndex": 1,
"toIndex": 2,
"type": "Success"
},
{
"fromIndex": 0,
"toIndex": 1,
"type": "Post telemetry"
},
{
"fromIndex": 2,
"toIndex": 3,
"type": "Success"
}
],
"ruleChainConnections": []
}
}
PathTypeDescription
ruleChainobjectRule chain header — must be present
ruleChain.namestringNon-empty rule chain name
ruleChain.typestringOne of the Rule Chain Types
ruleChain.rootbooleanMust be false — a contributed chain must never declare itself the root chain. Users decide whether to make it the root after install
metadataobjectRule chain body — must be present
metadata.nodesarrayThe default-processing nodes plus the nodes of your added flow
metadata.connectionsarrayWiring between nodes — technically optional, but any chain with more than one node needs it

Each entry in metadata.nodes represents one rule node on the canvas:

FieldDescription
typeFully qualified Java class name of the node (e.g. org.thingsboard.rule.engine.kafka.TbKafkaNode)
nameDisplay name shown on the canvas
debugSettingsPer-node debug configuration — see the recommendation for added nodes above
singletonModeWhether the node runs as a single instance across the cluster
queueNameProcessing queue override (null uses the chain’s queue)
configurationVersionNode configuration schema version — set by the export
configurationNode-specific configuration object
additionalInfoEditor metadata (position on canvas, etc.)

Each entry in metadata.connections wires two nodes together:

FieldDescription
fromIndexIndex of the source node in the nodes array
toIndexIndex of the target node in the nodes array
typeConnection label (e.g. Success, Failure, True, False) — depends on the source node’s output ports

The Listing step of the upload wizard collects the fields shown on the browse card and detail page. Name, the rule chain type, node count, node summary, Icon, and Color are auto-derived from your JSON (Name, Icon, and Color stay editable); everything else you set here. Version and changelog are set per upload — see Versioning and checksum.

FieldRequiredSourceNotes
NameyesruleChain.name in JSONEditable
IconyesAuto from ruleChain.typeEditable — see Icon and color
ColoryesAuto from ruleChain.typeEditable — hex color for the icon background
DescriptionyesTemplatePre-filled with a fill-in template. One-sentence summary, max 512 chars
CategoriesyesManualPick from Rule Chain Categories
Use CasesyesManualPick from Use Cases
Supported ThingsBoard versionnoManualMin () and optional max (<) version
Professional EditionnoManualToggle if the rule chain requires PE
TagsnoManualFree-form keywords

The ruleChain.type field must be one of: CORE, EDGE.

TypeUse for
COREA rule chain that runs on the main ThingsBoard server — the common case
EDGEA rule chain that runs on a ThingsBoard Edge instance — for edge message routing

Pick one or more categories that describe what your rule chain does. Allowed values: Alerting, Analytics, Automation, Data Processing, Device Connectivity, Integration.

Pick one or more IoT domains where your rule chain is useful. These are shared across the whole marketplace.

Allowed values: Air Quality Monitoring, Asset Tracking, Cold Chain, Drones, Environment Monitoring, Fleet Tracking, Health Care, Industrial Automation, Predictive Maintenance, Robotics, SCADA, Smart Building, Smart City, Smart Energy, Smart Farming, Smart Home, Smart Metering, Smart Office, Smart Retail, Solar Monitoring, Tank Level Monitoring, Waste Management.

Rule chains are displayed in the catalog with a Material Icon on a colored background instead of a preview image. When you upload your JSON, the platform picks a default icon and color based on the rule chain type — account_tree on blue for CORE, developer_board on teal for EDGE. You can keep the default or change it in the Listing step.

Icon. Pick a Material Icon that visually represents what the rule chain does. The icon picker shows a curated list of icons relevant to rule chains (e.g. account_tree, device_hub, hub, filter_alt, webhook). You can also search the full Material and MDI icon sets.

Good icon choices:

  • Alerting chains → notifications_active, warning, priority_high
  • Analytics chains → analytics, insights, query_stats
  • Automation chains → auto_mode, rule, autorenew
  • Data processing chains → filter_alt, transform, timeline
  • Device connectivity chains → device_hub, hub, lan
  • Integration chains → webhook, integration_instructions, sync_alt, cloud_upload

Color. Pick a hex color for the icon background. The picker provides 24 presets and a custom color picker.

Conventions across the catalog:

  • Red tones for alerting
  • Orange tones for automation
  • Blue tones for integration and connectivity
  • Green tones for transformations and analytics
  • Purple for machine-learning or custom-logic chains

The Readme step of the upload wizard collects the long-form documentation shown on your rule chain’s detail page. It is written in standard Markdown. The editor comes pre-filled with the recommended structure — the same sections all official ThingsBoard templates follow. Replace each placeholder with your content.

  1. # Who it's for — the audience and the questions they’re asking, one short paragraph
  2. # What it does — open by naming the added logic and the route it hangs off, e.g. “Extends the default ThingsBoard root chain with the Kafka-publishing flow on the Post telemetry path”. Then describe what each added node does. Optionally add a diagram image (SVG or PNG) — paste it directly into the readme editor. The diagram should show what the flow produces, not a picture of the rule chain itself — for example, an integration chain can show how the ThingsBoard message turns into the outgoing record, and an automation chain can show the resulting entity arrangement in each scenario
  3. # Requirements (optional) — include only when the chain can’t run until something outside it is in place: external systems, brokers, credentials, or platform features that must be pre-configured
  4. # How to set up — numbered steps written from the extends-the-root premise. Step 1 covers the two install-dialog paths: bind to a profile (the chain becomes that profile’s default rule chain), or install only — then set it as the root, or extract the added flow into a nested rule chain and connect it from the existing root. Follow with any post-install node configuration (broker addresses, credentials, endpoint URLs) and end with how to verify the flow works
  5. # How to customize — a bulleted list. Each bullet starts with the user’s goal in bold — what the user wants to achieve, not the control they will touch (e.g. **To publish to a different topic**, **To route per device type**). After the goal, a dash and the exact field and node to change

Example:

# Who it's for
Platform and DevOps engineers asking "how do I make device telemetry
available to my Kafka consumers?", "how do I feed analytics or data
lake pipelines from ThingsBoard without polling?" — when the goal is
to forward device telemetry into an existing Kafka topic as it
arrives.
# What it does
Extends the default ThingsBoard root chain with **the
Kafka-publishing flow** on the **Post telemetry** path:
- **Fetch Device Info** writes `deviceId`, `deviceName`, and
`deviceType` into the message data, so each Kafka record carries
the identity of the device it came from.
- **Send to Kafka** publishes the message to the `iot.telemetry`
topic, keyed by `deviceId` for per-device ordering.
# Requirements
- Apache Kafka cluster reachable from the ThingsBoard server
- Kafka topic created, or auto-creation enabled on the broker side
# How to set up
1. Click **Install** to open the install dialog. The dialog offers
two paths:
- **Bind to a profile** — enable **Set as profile default rule
chain** and pick the target device profile. The imported chain
becomes that profile's
[default rule chain](https://thingsboard.io/docs/user-guide/device-profiles/#default-rule-chain).
- **Install only** — leave the toggle off. After install, either
[set it as the root](https://thingsboard.io/docs/user-guide/rule-engine/#set-a-rule-chain-as-root)
— it already bundles the default routing plus the
Kafka-publishing flow — or extract the Kafka-publishing flow
into a
[nested rule chain](https://thingsboard.io/docs/user-guide/rule-engine/#create-a-nested-rule-chain)
and
[connect it](https://thingsboard.io/docs/user-guide/rule-engine/#forward-messages-to-another-rule-chain)
to the **Post telemetry** output of **Message Type Switch**.
2. Open the **Send to Kafka** node and set:
- **Bootstrap servers** — all broker addresses (e.g.
`kafka-1:9092,kafka-2:9092,kafka-3:9092`). Default: `localhost:9092`.
- **Topic pattern** — target topic. Default: `iot.telemetry`.
3. The chain runs on post telemetry uploads. Use
[**Check connectivity**](https://thingsboard.io/docs/user-guide/devices/#check-connectivity)
on any device whose telemetry routes through the installed chain
to confirm the record arrives in your Kafka topic.
# How to customize
- **To route different device types to different topics** — use
[templatization](https://thingsboard.io/docs/reference/rule-engine/templatization/)
in **Topic pattern** on the **Send to Kafka** node (e.g.
`iot.telemetry.$[deviceType]`).
- **To drop unwanted messages before they reach Kafka** — add a
[Filter](https://thingsboard.io/docs/reference/rule-engine/nodes/filter/)
node before **Send to Kafka** (e.g. a filter script on data
or metadata fields).
- **To add more context to the Kafka record** — add an
[Enrichment](https://thingsboard.io/docs/reference/rule-engine/nodes/enrichment/)
node before **Send to Kafka** (e.g. customer, tenant, asset,
or related-entity data).
- **To tune producer reliability and throughput** — adjust the
**Producer settings** on the **Send to Kafka** node (retries,
batch size, linger time, buffer memory, acks). Defaults work
for most deployments.

Each rule chain version is identified by a semver string (e.g. 1.0.0) plus a changelog entry, and an SHA-256 checksum computed from:

  • ruleChain.name
  • ruleChain.type
  • Every node’s type, name, and configuration
  • Every connection’s fromIndex, toIndex, and type

This means any structural change — adding a node, changing a node’s configuration, rewiring connections — changes the checksum and requires bumping the version. Changes to description, icon, or color do not change the checksum.

Always bump the version when uploading a changed rule chain JSON.

When you upload a new version, pair the version bump with a changelog entry — a short note that tells users exactly what changed since the previous version, so they can decide whether, and how, to upgrade.

Summarize what changed since the previous version: new features, bug fixes, breaking changes, and any migration notes users need to know.

Group your entry under these headings, and include only the ones that apply:

HeadingWhat goes here
New featuresNew capabilities, configuration options, or behavior added in this version
Bug fixesDefects corrected since the previous version — describe the symptom users saw, not the internal cause
Breaking changesAnything that changes existing behavior in a way that can disrupt an installed copy — renamed keys, removed options, changed defaults
Migration notesThe concrete steps an existing user must take to move from the previous version to this one
  • Lead with the user impact. Describe what the user can now do, or what no longer breaks — not how you implemented it.
  • Be specific. Name the exact keys, fields, settings, or outputs that changed. “Renamed the output key from temp to temperature” is actionable; “improved naming” is not.
  • One change per bullet. Keep each item to a single, scannable line.
  • Flag breaking changes loudly. If an upgrade can disrupt an installed copy, say so explicitly and pair it with a migration note.

Pair every entry with a semantic version bump — patch (1.0.1) for fixes, minor (1.1.0) for backward-compatible features, major (2.0.0) for breaking changes.

## 2.0.0
### Breaking changes
- Renamed the output key from `temp` to `temperature` to match the
ThingsBoard telemetry convention.
### Migration notes
- Update any dashboards, alarm rules, or downstream calculated fields
that read the `temp` key to read `temperature` instead.
### New features
- Added an optional `humidity` argument; when present, the formula
now also computes a `dewPoint` output.
### Bug fixes
- Fixed missing output when the input telemetry arrived as a string
instead of a number.

Once you complete the upload wizard and click Submit, your version enters the IoT Hub review queue. The ThingsBoard team checks every submission before it goes live.

To see the current status of your submission, open the Creator Portal and go to Items. Find your item in the list and click the Manage Versions icon in its row. The Versions page lists every version you have uploaded with a Status column that updates in real time.

StatusMeaning
Pending ReviewYour version is in the review queue and has not been evaluated yet
ApprovedYour version passed review and is now live on IoT Hub
RejectedYour version did not pass review — see the reviewer comment for details

Reviewers verify that the submission meets the same criteria as the Pre-Upload Checklist: the rule chain processes messages correctly, the JSON is valid, and the listing and readme give users enough context to evaluate and use the chain.

The Status column will show Rejected. Open the version details to read the reviewer’s comment, which explains specifically what needs to be fixed.

To resubmit:

  1. Fix the reported issues in your local package.
  2. Return to Items → Manage Versions for your item.
  3. Click + Upload new version and complete the wizard with the corrected package.
  • Rule Chains — how end users discover and install a rule chain