Provisioning a single IoT device is easy. Provisioning ten thousand of them across factories, test stations, warehouses, and dozens of customer sites is a different problem entirely.
What separates well-run deployments from painful ones is recognizing that provisioning isn't a single step. It's a lifecycle that follows the device from its first appearance on the manufacturing line all the way to the customer's loading dock, with different mechanisms layered on at each stage.
If you get the architecture right, you can move tens of thousands of units through configuration, testing, and shipment without any of them requiring an engineer to touch them! Get it wrong? Well, every new customer onboarding becomes a custom integration project, with operators flashing config files, technicians editing spreadsheets, and a long tail of support tickets!
In this post we'll walk through a complete, four-stage enterprise provisioning workflow built around Blues Notehub. It combines four mechanisms (QR codes, environment variables, batch jobs, and named fleets) and uses each one at the moment when it's most useful.
The pattern provided is meant to be general and you should adapt it to your own product, manufacturing flow, and customer model.
The Building Blocks
Before we walk through the provisioning lifecycle, let's start with the building blocks:
- Hardware-specific QR codes. Every Notecard ships with
QR-coded stickers
that resolve through
qrgo.org/id/[deviceUID]/[pin]to a URL you control via your Notehub project's Device Dashboard URL setting. You can set up your cloud backend to determine what that URL resolves to in your system (and you can change that at any point). - Environment variables. A
key-value configuration mechanism
that lives in Notehub and is pulled down by the device on its next sync. You
can set them at the device, fleet, or project level in the Notehub UI and/or
via the Notehub API, and the
host MCU reads them with a simple
env.getrequest to the Notecard. - Batch jobs. A JSON-driven way to perform bulk operations across many devices in a single submission (e.g. fleet assignment, environment variable updates, and reporting) without writing a script that loops through API calls. Batch jobs also support dry runs and store a full audit history of every execution.
- Named fleets. Fleets are groupings of
devices in Notehub. As a device progresses through its lifecycle, your
backend moves it between named fleets like
manufacturing,in-test,inventory, and one-per-customer fleets such ascustomer-acme. Fleet membership is how the device's lifecycle state is tracked in Notehub itself, and it determines which environment variables a device inherits and which automation rules may apply to it.

Stage 1: Manufacturing
A freshly assembled unit comes off the line. The Notecard inside already has its
cryptographic credentials baked in, as every Notecard ships with certificates
installed during manufacturing, so it can power on and authenticate against
Notehub with no configuration step on your part. But from your backend's
perspective, the device is still an anonymous
DeviceUID like dev:000000000000000.
It doesn't know which physical product it lives in, who built it, or who's
buying it.
An operator on the line scans the Notecard's QR code with their scanning device. Because your backend sees that the device as "unprovisioned", the backend can automatically redirect the URL to a manufacturing-specific web app. This might be a web app with an embedded camera view that prompts the operator to scan a second QR code, this one on the enclosing product itself, encoding the product's serial number.
The web app then records the association between DeviceUID and product serial
number in your own database and adds the device to a manufacturing fleet in
Notehub via the
Add Device to Fleets API.
That fleet membership is the device's lifecycle state from this point forward,
and it's what your backend will key off of when deciding what subsequent QR
scans should resolve to.
From this moment on, the two stickers mean the same thing: scanning either one resolves to the same device record in your system.
Stage 2: Automated Testing
The unit gets rolled to the test station. A technician scans the QR code, and
because your backend sees the device is in the manufacturing fleet, the URL
now resolves to a "begin test" screen. The technician confirms, and that action
fires an API call from your backend that removes the device from the
manufacturing fleet and adds it to an in-test fleet in Notehub. Power is
applied, peripherals are exercised, and the host MCU streams telemetry to
Notehub like any deployed device would.
Whenever a technician scans the QR code now, the same URL resolves somewhere
new, like a live test status dashboard, because your backend knows this device
is in test. The technician can watch the test suite progress in real time:
sensor readings, error counts, and the result of each automated check. Your
backend can drive the tests themselves by updating environment variables (e.g. a
test_mode flag or a target_setpoint value) and verify that the host firmware
reacts correctly on the next sync.
When the test suite completes, the technician scans the QR code one last time.
The destination is now an approve/reject screen. Approving moves the device
from the in-test fleet into an inventory fleet; rejecting moves it into a
rework fleet instead. Either way, the fleet change immediately changes what
subsequent scans will resolve to.
This is a great place to layer in
Notehub Smart Fleets. A Smart Fleet's
membership is determined automatically by a JSONata expression evaluated
against incoming events, so you can define a failed-test Smart Fleet that
auto-adds any device emitting a particular error code or out-of-range sensor
reading during the test phase. Failing units get flagged for review without
anyone having to actively monitor them!
Stage 3: Inventory and Customer Assignment
The approved unit is now ready to move to inventory. Subsequent QR scans now resolve to a warehouse-management view. When a customer order comes in for 100 units, a warehouse operator picks them, scans each one to confirm assignment, and selects the customer in your backend.
At this point, your backend can kick off a Notehub batch job. The job moves all
100 devices out of the inventory fleet, into a customer-specific
customer-acme fleet, and writes the customer's environment variables (e.g.
region, dashboard URL, firmware-relevant configurations) in a single operation:
{
"comment": "Onboard 100 devices to Customer ACME",
"select": {
"devices": ["dev:864009060869498", "dev:864009060869501", "..."]
},
"default_requests": {
"fleets_to_join": ["customer-acme"],
"fleets_to_leave": ["inventory"],
"vars_to_set": {
"customer_id": "ACME",
"region": "us-east",
"dashboard_url": "https://portal.acme.example.com"
}
}
}One JSON file, one batch job submission, 100 devices ready for shipment! Compare this to making 100 fleet-assignment API calls plus 300 environment-variable POST requests, this is the difference between a workflow that scales and a workflow that doesn't.
Always run batch jobs as a "dry run" first when they will modify device state. Notehub previews the effects of a batch job without making any changes, which makes it easy to catch a bad device list or a typo in a fleet name before it propagates to multiple units.
Stage 4: End Customer Deployment
When the end customer receives the unit and powers it on for the first time,
Notecard syncs with Notehub. The customer-specific environment variables you set
in Stage 3 download to the host MCU (typically read via an env.get API request
shortly after boot) and the firmware uses them to identify itself to the
customer's environment. No configuration steps required, no on-site setup, no
manual intervention from the field.
When an employee at the customer's site scans the QR code on the unit, that same sticker (yes, the same URL it's had since it left the line!) can now resolve to your customer portal for that organization. The same physical QR code that was a manufacturing app two months ago is now a customer-facing support and configuration UI, with no one having printed a new sticker. And six months from now, if the customer is migrated to a new portal, you can change the URL without anyone touching the device.
There's an intentional tight coupling between the state of the device in your backend, the environment variables on the Notecard, and the UI a person sees when they scan the QR code. That coupling is what makes the whole workflow feel coherent: any party in the supply chain or customer organization sees an interface that matches what the device is supposed to be doing right now.
Why This Process Works
Four things make this lifecycle possible, and they're the takeaway for designing your own customized provisioning workflow:
The QR code is a stable identifier with a dynamic destination. The URL on the sticker never changes; what it resolves to is entirely under your backend's control. One sticker, one identifier, infinite contextual destinations across the device's lifetime.
![Diagram showing a single physical QR code on the left, the URL pattern qrgo.org/id/[device]/[pin], a backend decision that checks the device's current fleet, and four color-coded destination UIs corresponding to the manufacturing, in-test, inventory, and customer-acme fleets](https://res.cloudinary.com/blues-wireless/image/fetch/f_auto,c_limit,w_3840,q_auto/https://dev.blues.io/_next/static/media/qr-routing.a9af2b60.png)
Environment variables handle device configuration. Anything the host firmware needs to know about the unit's identity, customer, or operating parameters is written as an environment variable in Notehub and downloaded automatically on the next sync. Configuration becomes centralized, auditable, and changeable after the device has already shipped.
Batch jobs operate on groups, not individuals. Customer onboarding, fleet reassignment, and bulk configuration changes happen in one operation rather than a loop of API calls. As your deployment scales from hundreds to hundreds of thousands of units, this matters more, not less.
Named fleets are the lifecycle state machine. A device's current fleet is its lifecycle state, visible to your backend, to dashboards, and to anyone who scans its QR code. Moving a device between fleets is how state transitions happen, and Smart Fleets let some of those transitions happen automatically based on the device's own telemetry.
Wrapping Up
The result is a workflow that feels almost magical when you watch it run: a unit on the manufacturing floor, a unit on the test bench, a unit in inventory, and a unit at a customer site are all the same physical product with the same QR code on its side, but every party in the supply chain sees an interface tailored to where the device is right now in its life.
To dig deeper, consult our Fleet Administrator's Guide for a full walkthrough of provisioning mechanisms, and our batch jobs guide for more on designing JSON job files for your own bulk operations.

