Device Self-Registration
Creating every device by hand, or importing a file of them, works when you know the fleet in advance. Self-registration is for the other case: a fleet that comes off a production line with one shared secret flashed in, where each unit introduces itself the first time it powers on. The device sends its serial number over HTTPS, makes up its own credential, and gets back its device token and MQTT client id. Nothing about the device has to be entered first, unless you want it to be.
This page is the operator's side: what to set up, how to start a fleet, and what to do when something goes wrong. The exact request, responses and error codes, which firmware authors need, are in the device protocol reference.
How it fits together
A provisioning profile is the fleet's shared entry point. It holds:
| Field | Meaning |
|---|---|
| Provision key | A name the fleet presents, unique in the tenant. Not secret. |
| Provision secret | Generated by the server (256 random bits), shown once when you create or rotate the profile, and stored only as a digest. Flash it into the fleet's firmware. |
| Strategy | ALLOW_NEW creates a device the platform has never heard of, and also adopts one you earmarked. CHECK_PRE_PROVISIONED registers only devices that already exist and were earmarked for the profile. |
| Device type | The type of every device the profile registers, and the only type it will adopt. |
| Max devices | Optional cap on how many devices the profile registers. Without one, the profile is uncapped. |
| Enabled, expires at | A disabled or expired profile refuses every registration immediately. |
The device holds its own credential: it generates 32 random bytes, encodes them as base64url, keeps them, and sends them in the registration request. The platform stores a digest, so nobody, an administrator included, can read it back. The credential is an access token: the device connects over MQTT with the username {tenant}:{credential} and no password, and the client id the response names.
Set up a profile
- Create the profile in the console under Connect → Provisioning Profiles, or with
createProvisioningProfile. Pick the device type, the strategy, an optional cap and, if the fleet ships in batches, an expiry. - Copy the secret from the dialog that follows. It is shown once. If you lose it, rotate it; the old one stops working at once.
- Make sure the API ingress is enabled. The endpoint is served on the same
/api/device-managementroute as the GraphQL API. Withingress.enabled: falsethe instance has no public path to it. - Check the client address setup if you run your own ingress controller or a cloud load balancer: Client addresses and trusted proxies. A mistake there does not stop registration, but every device then shares one request allowance.
Rotate required
A profile created before the server generated secrets has a secret an operator chose. It is refused (401 PROVISIONING_FAILED, like any wrong secret) until you rotate it, and the console marks it Rotate required. Rotation issues a new secret, shown once, and the old one stops working at once. Update the firmware, then devices register with the new one.
Earmarking
An earmark says "this existing device may be taken over by this profile". A profile adopts a device only when it was earmarked for it, under either strategy. That is deliberate: without it, anyone holding the fleet secret and an operator-made device's serial number could take the device over.
Earmark a device when you create it:
- in the console, with the Expected provisioning profile field of the device form;
- in a CSV import, with the
provisioningProfileTokencolumn (columns); - over GraphQL, with
provisioningProfileTokenoncreateDeviceorimportDevices.
An earmark can only be set when the device is created. Nothing sets one on an existing device, so a device created before you set up the profile cannot be adopted by it. The profile must exist in the tenant and be of the device's type. A device's detail page shows how it came onto the platform and offers Reset provisioning; the device list can be filtered by the profile that issued its credential or by the profile it is expected for (provisioningProfile and expectedProvisioningProfile in DeviceSearchCriteria).
Deleting a profile does not delete earmarks: the devices fall back to having none, and are refused (DEVICE_NOT_EARMARKED) from then on.
What a device sees
With a profile whose key is acme-sensors-2026 and a device with serial SN-000123, the firmware does the equivalent of:
# once, on the device: make the credential and keep it BEFORE sending anything
openssl rand -base64 32 | tr '+/' '-_' | tr -d '=\n' > /data/credential
curl -sS https://dc.example.com/api/device-management/provisioning/acme/register \
-H 'Content-Type: application/json' \
-d '{
"provisionKey": "acme-sensors-2026",
"provisionSecret": "'"$PROVISION_SECRET"'",
"externalId": "SN-000123",
"credentialSecret": "'"$(cat /data/credential)"'"
}'
{"outcome":"CREATED","tenant":"acme","deviceToken":"pv-SN-000123-06e08144e3e8","externalId":"SN-000123","credentialType":"ACCESS_TOKEN","mqtt":{"clientId":"<instanceId>:acme:pv-SN-000123-06e08144e3e8"}}
You can try the whole flow without writing firmware. dcctl device register plays the device: it generates the credential, saves it to the file named by --material-file before it sends anything, follows the retry rules, and prints the device token and client id. Run it again and it reports REPLAYED.
dcctl device register --server dc.example.com --tenant acme \
--key acme-sensors-2026 --secret-file ./provision.secret \
--external-id SN-000123 --material-file ./SN-000123.credential
Two rules matter more than the rest:
- Save the credential before the first send. The platform keeps only a digest. A device that loses its credential cannot recover it by registering again: it presents new material for a device that already holds a credential, and is refused with
409 ALREADY_PROVISIONED. An operator then uses Reset provisioning so it can register again. - Retry with the same credential, and wait. A registration whose answer was lost is recognised as a replay (
200 REPLAYED) and is safe to repeat. On429and503, wait theRetry-Afterplus a random jitter; do not hammer. The retry contract lists which answers to retry and which need a person.
Rotation and overlap
rotateProvisioningProfileSecret (the console's Rotate secret) is a hard cutover: the old secret stops working on the next request, with no overlap. Devices that already registered are unaffected, because they connect with their own credential, not the fleet secret.
For a rotation that needs an overlap, for example while a factory still flashes the old secret, create a second profile with its own key and secret, and set the old profile's expiry.
If the secret leaks
The secret is the whole trust anchor for first contact: whoever holds it can register a device that has not registered yet. What that means depends on the strategy. Under ALLOW_NEW, any serial number nobody has registered yet. Under CHECK_PRE_PROVISIONED, only the serials you earmarked and that have not registered. The maxDevices cap bounds either.
To recover:
- Rotate the secret, so the leaked one stops working.
- Revoke the devices the profile issued if some of them are not yours (
revokeProvisionedDevices, the console's Revoke devices). Their credentials are disabled and their live connections are ended. - Find what registered and when. Filter the device list by the profile (
provisioningProfile) and look at each device's creation time. Registrations are also in the audit journal asdevice.provision.createdanddevice.provision.adopted, attributed toprovisioning-profile:<token>. A replay is counted but not journaled. - Reset the devices you do own (
resetDeviceProvisioning) so they can register again with the new secret. A revoked device stays refused until you do.
Disable a device, do not delete it
Disable a unit you want kept out. Do not delete it. A deleted device's serial number is unknown again, so under ALLOW_NEW the next unit to present it registers as a new device.
Deleting a profile and its devices
deleteProvisioningProfile is refused while any device still holds a credential the profile issued, revoked ones included: "revoke the profile's devices first, then reset their provisioning or delete them". This keeps a device's record of where its credential came from from pointing at nothing. Likewise a device type cannot be deleted while a provisioning profile uses it.
Lowering maxDevices below the number already registered only refuses further registrations; it removes nothing.
Operating it
- Limits. Registration is rate limited per source address, per tenant and per replica, and each replica serves at most 64 requests at once. The values and what each answers are in the limits. The per-tenant ceiling is part of the tenant's governance and can be set on its tier or per tenant (
provisioningRegistrationsPerSecondandprovisioningBurst). - Metrics and alerts.
provisioning_requests_totalcounts every request by result, including theauth_failedflood a probe produces. Three alerts watch it:ProvisioningAuthFailuresSustained,ProvisioningRateLimitedandProvisioningMisconfigured. The counters are in the metrics reference. - Secret-based registration is HTTPS only. A device that holds the fleet secret cannot register by connecting over MQTT. A device with a client certificate can: a certificate profile registers it on its first MQTT connect, with no secret.
- MQTT_BASIC is not issued. Self-registration mints an
ACCESS_TOKENcredential only. A profile whose credential type this build cannot mint refuses with501.