Skip to main content

Enrolment tokens

What an enrolment token is, how long it lasts, where the installer reads it from, how to tell an install worked, and every failure message you might see.

Written for whoever runs IT9 min readUpdated

An enrolment token is the one-time coupon a freshly installed sensor redeems to join your organisation. It is not the credential the sensor uses afterwards. Getting those two things straight is most of what there is to understand here, and confusing them is the most common support ticket this part of the product produces.

This article covers the format and lifetime of a token, where to create one, how the installer receives it, how to confirm an install actually worked, and the exact text of every failure you might see. It also names two error messages that do not exist, because looking for a message that was never written is a bad afternoon.

Two different secrets

The enrolment token and the sensor token are different objects with different lifetimes. Neither can do the other's job.
Enrolment tokenSensor token
Created byYou, in the consoleThe platform, as the result of enrolment
Looks likezqe_ followed by 64 hex charactersBare hex, no prefix
ScopeOne organisation, an expiry, optionally a product, a platform and a use capExactly one machine
May doCreate one sensor, then it is spent or expiresSend telemetry and collect response tickets
May neverAuthenticate telemetryEnrol anything

The zqe_ prefix is not decoration. It is how the sensor works out, from its own config file alone, whether it is holding a coupon it still has to redeem or a credential it can already use. That single character sequence is what makes deferred enrolment possible.

Format, lifetime and caps

  • Format: zqe_ plus 64 hexadecimal characters, from 32 random bytes.
  • Only the hash is stored. The plaintext token exists exactly once, in the response that creates it, and is never recoverable afterwards. The console shows you the first twelve characters — zqe_ and eight more — so you can tell two tokens apart in a list.
  • Default lifetime: 7 days.
  • Maximum lifetime: 90 days.
  • Optional use cap. Unset means unlimited. Set it to the number of machines in the batch.
  • Optional product, sensor or pam-agent, defaulting to sensor.
  • Optional platform, one of any, windows, linux or darwin, defaulting to any.

Ask for a longer life than 90 days and the request is refused, not quietly shortened:

ttl_seconds may not exceed 7776000 (90 days) — enrolment tokens are short-lived by design

Refusing is the deliberate choice. Someone who asked for a year has made an assumption about how this works; issuing them a 90-day token instead would let them discover the difference three months later, when their fleet stops enrolling and nobody remembers why.

One consequence of how scope is enforced is worth knowing before you narrow a token: the product and platform restrictions are checked after a use is counted. That is on purpose, so nobody can probe a token to learn what it is scoped to without spending one. It does mean a Windows-scoped token typed into a Linux install burns a use and then fails.

Where to get one

In the console: Settings -> Endpoints -> Enrolment tokens. Creating one returns the plaintext token together with a ready-made install command for each platform, with the token, the download URL and your API URL already substituted. That response is the only place the full token ever appears.

The same operations exist on the API for anyone automating a rollout. They are ordinary tenant-scoped routes and they need the endpoint permissions from your role — a role without them gets a 403 and the message insufficient permissions. See Roles and permissions.

The enrolment-token API. Everything except the unattended enrolment route requires a signed-in user with the matching permission.
RouteNeedsPurpose
POST /api/v1/organizations/{orgID}/xdr/enrollment-tokensxdr:endpoints.writeCreate a token
GET /api/v1/organizations/{orgID}/xdr/enrollment-tokensxdr:endpoints.readList tokens
POST /api/v1/organizations/{orgID}/xdr/enrollment-tokens/{tokenID}/revokexdr:endpoints.writeRevoke a token
GET /api/v1/organizations/{orgID}/xdr/enrollment-tokens/{tokenID}/sensorsxdr:endpoints.readDid the install work?
POST /api/v1/xdr/sensor/enrollThe zqe_ token itself, as a bearer tokenWhat the installer calls

That last route carries no organisation in its path, and that is a security property rather than an oversight. The tenant comes from the token. Taking the organisation from the URL would let anyone holding any valid token enrol into any tenant they could name.

How the installer receives the token

Windows uses hidden MSI properties supplied through the installer API, followed by standard input to the native enrollment command. The generated Windows command collects the token at a masked prompt. Linux installers accept environment variables or a protected token file.

Environment variables read at install time and at run time. The install-time and run-time names are different variables; mixing them up is an easy support ticket. QX_ is the current prefix as of sensor 1.6.0; ZQ_ still works as a fallback on this release for anything already scripted against it.
VariableRead byMeaning
QX_ENROLL_TOKEN (or ZQ_ENROLL_TOKEN)Linux .deb / .rpm and macOS package scriptsEnrolment token at install time
QX_API_URL (or ZQ_API_URL)the same scriptsAPI base URL. Use the console origin you were given at enrolment, e.g. https://app.qorionix.com
SENSOR_ENROLL_TOKENthe Windows MSI, as a propertyWindows equivalent of QX_ENROLL_TOKEN
SENSOR_API_URLthe Windows MSI, as a propertyWindows equivalent of QX_API_URL
QX_ENROLLMENT_TOKEN (or ZQ_ENROLLMENT_TOKEN)the running serviceRun-time credential override, not an install-time input
QX_CONFIG_PATH (or ZQ_CONFIG_PATH)the running serviceOverrides the compiled-in config path
QX_SERVER_URL (or ZQ_SERVER_URL)the running serviceOverrides api_url
QX_SENSOR_ID (or ZQ_SENSOR_ID)the running serviceOverrides the sensor identity

On two of the three platforms an environment variable is not a dependable delivery mechanism. apt and dnf do not reliably forward the invoking environment to package maintainer scripts, and a macOS package opened from Finder runs its scripts with no useful environment at all. That is why file-based input exists:

The two files the installer will read if they exist
/etc/qorionix/enrollment-token     (mode 0600)
/etc/qorionix/api-url

Both are whitespace-stripped when read, so a trailing newline or a CRLF from an MDM-pushed file is handled rather than becoming a mystifying rejection. The token file is deleted once enrolment reaches a terminal outcome, and kept where a corrected re-run would still need it.

What ends up in the config file

Enrolment writes the machine's config file in full — it is rendered, not merged. The file is written atomically at mode 0600, because the running service reads it and a torn write during a repair install would otherwise leave the agent holding half a token. On Linux the package then relaxes it to root:qorionix-sensor mode 0640.

Config keys the sensor actually reads. Anything not on this list is written into the file but never parsed, so changing it has no effect.
KeyWhat it holds
api_urlThe API base URL.
enrollment_tokenThis machine's credential once enrolment succeeds. Treat as a secret.
sensor_idThe machine's identity in the platform. Present only after enrolment completes.
organization_idEchoed back so you can see which tenant the machine joined.
tls_insecure_skip_verifyTest rigs only. Not for production.

The shipped default config also contains a channels: block, poll_interval_seconds, cache_path and log_path. No code reads any of them. They look like tuning knobs and they are not, and we would rather say so plainly than have you spend an afternoon adjusting a number nothing consumes.

How enrolment actually works

The installer posts the machine's hostname, platform, architecture, sensor version and a stable host fingerprint to POST /api/v1/xdr/sensor/enroll, carrying the zqe_ token as a bearer token. A successful enrolment answers 201 Created with the sensor id, the machine's own credential, the organisation id, and a reenrolled flag.

The fingerprint is derived from the machine's machine-id where one exists, otherwise its lowest non-loopback MAC address, otherwise just the hostname. It is deliberately stable across reinstalls, because it is the key that decides whether a repair install produces the same endpoint or a duplicate one.

The installer's exit code is a contract, not incidental, and both the Linux package scripts and the MSI branch on it:

Enrolment exit codes. The split between a transient failure and a credential failure is the important one.
CodeMeaningWhat happens next
0EnrolledNothing to do.
2Token empty or malformedEnrolment was not attempted. Fix the token and re-run.
3Token rejectedFatal. A human must issue a new token.
4Network or server problemConfig is stamped; the service will keep retrying.
5Could not write the config fileA local permissions or disk problem.

A network failure is not fatal and a credential failure is, because no amount of retrying fixes a token that has been spent — and silently installing a sensor that can never enrol is the single worst outcome for an admin with no security team. Note that the package scripts themselves always exit 0 regardless, so a clean apt-get or a green MSI does not prove enrolment happened.

Where the installer could not reach the API, the token is stamped into the config and the service finishes the job on first start, retrying after 5 seconds, 30 seconds, 2 minutes, 10 minutes and 30 minutes, then every 30 minutes indefinitely. Only the first five failures are logged; past that, the endpoint's absence from your console is the signal you will actually act on. A credential rejection stops the loop at once.

Confirming an install worked

Ask the token, not the machine. GET /api/v1/organizations/{orgID}/xdr/enrollment-tokens/{tokenID}/sensors returns the token's use count, its cap, the sensors it created, and two separate counters that are the whole point of the endpoint:

`awaiting_first_event`
A sensor row exists — the token was spent — but no telemetry has ever arrived from that machine. The install ran; the agent is not reporting.
`checked_in`
Telemetry has arrived. This machine is genuinely alive.

A sensor row proves only that the token was spent. Only a check-in proves the agent is running. Collapsing those two into one number would let a fleet of installed-but-dead agents read as a successful rollout. An unknown token id returns a 404 rather than an empty list, so a mistyped id cannot render a permanently cheerful waiting…. And use_count can legitimately exceed the number of sensors, because a machine that re-enrols spends another use without creating another endpoint.

The failure messages, verbatim

Six token failures come back as 401, one per cause:

Enrolment rejections (HTTP 401)
enrolment token not recognised
enrolment token has been revoked
enrolment token has expired
enrolment token has reached its maximum number of uses
enrolment token was not issued for this product
enrolment token was not issued for this platform

Most of them arrive with the detail attached, which is usually what tells you what to do:

The same rejections with their detail
enrolment token has been revoked (revoked 2026-09-01T10:22:14Z)
enrolment token has expired (expired 2026-08-25T09:00:00Z)
enrolment token has reached its maximum number of uses (3 of 3 used)
enrolment token not recognised: token state changed during enrolment, please retry
enrolment token was not issued for this product: token was issued for pam-agent, this is the sensor enrolment endpoint
enrolment token was not issued for this platform: token is for windows, this host is linux

If no token reached the installer at all, the answer names both variables so you do not have to guess which platform uses which:

no enrolment token supplied — pass one with SENSOR_ENROLL_TOKEN (Windows) or ZQ_ENROLL_TOKEN (Linux/macOS)

On the machine itself, the enroll command prints the same situations in longer form:

Messages from the enroll command
enroll: no enrolment token supplied.
        Create one in the Qorionix console under Settings -> Endpoints -> Enrolment tokens,
        then pass it with --token.
enroll: the Qorionix server rejected this enrolment token.
        Reason: <detail>
        Fix:    issue a fresh token in the console under
                Settings -> Endpoints -> Enrolment tokens, then reinstall.
enroll: could not reach <api> right now (<err>).
        The agent is installed and will keep trying to enrol in the background.
        If this host never appears in the console, check outbound HTTPS to <api>.

And on success:

Messages from a successful enrolment
enroll: enrolled as sensor <id>
enroll: re-enrolled (this host was already known) as sensor <id>
enroll: config written to <path>
enroll: this machine should now appear under Endpoints in the Qorionix console

Two messages that do not exist

Both of these are things an experienced admin will search for. Neither string is in the product, so searching for them wastes time that this section is meant to save.

There is no clock-skew error
A machine whose clock is badly wrong fails TLS certificate validation before it ever reaches our enrolment logic, so the failure surfaces through the generic transient path: enroll: could not reach <api> right now (<err>), with a Go TLS error inside the brackets, and exit code 4. The sensor will then retry on the usual backoff forever, because as far as it can tell this is a network problem. If a host reports an unreachable API but the network is demonstrably fine, check the system clock and the time source before anything else.
There is no duplicate-host error
Enrolling a machine that is already known is not an error and produces no warning. The existing endpoint is updated in place — hostname, platform, architecture refreshed, any revocation cleared — and the response is flagged as a re-enrolment, which the installer prints as re-enrolled (this host was already known). You will not get a duplicate endpoint, and you will not get a message telling you it happened, beyond that one line. The token's use count still goes up.

Revoking a token

Revoking an enrolment token stops it creating any more endpoints. It does not disable the machines that already enrolled with it — each of those holds its own per-machine credential and is unaffected. To stop a specific machine reporting, revoke that sensor rather than the token it came from; a revoked sensor is refused at the door with sensor auth: sensor revoked.

The usual habit is one token per rollout batch, capped at the size of the batch, revoked when the batch is done.

What to do next

  1. Installing the sensor on Windows — the signed MSI and the exact silent command.
  2. Installing the sensor on Linux — the deb and rpm forms, including the token-file form for configuration management.
  3. The endpoint list and endpoint health — where an enrolled machine shows up, and what it means when it stops.

Was this article wrong?

If a procedure here does not match what you see, or a limit we described has changed, tell us and we will fix the page. Email us about this article, or see how to get help if you need an answer rather than a correction.

Everything in endpoints and response