Provisioning QR codes
Create a QR code that enrolls a new or factory-reset Android device into your company with a given policy.
How it works
Each request creates a new enrollment token for the policy and returns a QR code that carries it. The code stops working 24 hours after it is created; the response gives the exact time in expiresAt. Create a code when someone is about to set up devices, and create a new one instead of reusing an older one.
To use the code, tap the welcome screen of the device's setup wizard six times, connect to Wi-Fi if asked, and scan it. A device can only be enrolled this way when it is new or has been factory reset.
VR-only policies cannot be provisioned with a QR code: their devices are provisioned with Nomid Ops over USB.
Requirements
- An API key with the PROVISIONING permission, combined with POLICIES_READ. Only a portal user who is allowed to provision devices and has access to all policies can create a key with PROVISIONING.
- The company must have finished its Android Enterprise enrollment.
- MCP clients that connect with Sign in with Nomid instead of an API key need the mcp:provisioning scope, and the company's provisioning setting under AI agent access in the portal must be turned on.
Endpoint
The path is relative to the API base URL and needs the X-API-Key header. A successful request returns 201 Created.
| Method | Endpoint | Description |
|---|---|---|
| POST | /policies/{policyId}/provisioning-qr-codes | Create a provisioning QR code for a policy, with a new enrollment token |
Request body
Every field is optional: send an empty JSON object for a code without Wi-Fi and with the default personal usage. An unknown field returns 400.
| Field | Values | Description |
|---|---|---|
| personalUsage | UNSPECIFIED, ALLOWED, DISALLOWED, USERLESS | How the device is used: ALLOWED for a work profile on a personally owned device, DISALLOWED for a fully managed company-owned device, USERLESS for a dedicated device without a user account. UNSPECIFIED is the default when omitted. |
| wifiSsid | A Wi-Fi network the device joins during setup. Up to 32 bytes in UTF-8. Spaces around it are kept as part of the name. | |
| wifiPassword | The password of wifiSsid, up to 63 bytes in UTF-8. A WPA password has at least 8 bytes. A WEP key is 5 or 13 ASCII characters, or 10 or 26 hexadecimal digits. Never returned. | |
| wifiSecurity | NONE, WPA, WEP | The security of wifiSsid. Defaults to WPA when a password is given and NONE otherwise. NONE takes no password, and WPA and WEP need one. |
| wifiHidden | Whether wifiSsid is a hidden network. Defaults to false. |
wifiPassword, wifiSecurity and wifiHidden need wifiSsid.
Response
| Field | Description |
|---|---|
| policyId | The policy's path name, the policyId devices enroll with. |
| policyName | The policy's display name. |
| personalUsage | The personal usage mode of the enrollment token. |
| expiresAt | When the QR code stops enrolling devices. |
| wifiSsid | The Wi-Fi network the device joins during setup. Omitted when none was given. |
| image | The QR code image: mimeType (image/png) and data, the image bytes in base64. |
| portalUrl | A link to the policy in the Nomid portal. |
Example
Create a code for a fully managed device that joins a WPA network during setup. jq builds the body from an environment variable, so the password is escaped correctly and does not end up in your shell history.
curl -X POST "https://api.nomid.tech/emm/api/v1/policies/p7k2m9qa4xz/provisioning-qr-codes" \
-H "X-API-Key: $NOMID_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg password "$WIFI_PASSWORD" \
'{personalUsage: "DISALLOWED", wifiSsid: "Warehouse", wifiPassword: $password}')" HTTP/1.1 201 Created
{
"policyId": "p7k2m9qa4xz",
"policyName": "Warehouse scanners",
"personalUsage": "DISALLOWED",
"expiresAt": "2026-10-11T14:30:00Z",
"wifiSsid": "Warehouse",
"image": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAA..."
},
"portalUrl": "https://portal.nomid.tech/#/acme/acme/policy/p7k2m9qa4xz"
} Decode data from base64 to get the PNG. The image data is shortened here.
Error: VR-only policy
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "Policy q4vr8headset is a VR-only policy: its devices are provisioned with Nomid Ops over USB, not with a QR code.",
"instance": "/emm/api/v1/policies/q4vr8headset/provisioning-qr-codes",
"code": "unsupported",
"retryable": false
} Errors
Once the API key is authenticated, errors use application/problem+json with two extra fields: code, a stable identifier to branch on, and retryable, which says whether the same request can be sent again. A body that cannot be read is the exception, described in the 400 row. A missing or invalid key gets the shared 401 response, a small JSON object with error, message and status. The MCP tool returns the same code and message as a tool error.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_arguments | An option is invalid: an unknown personalUsage or wifiSecurity value, a Wi-Fi field without wifiSsid, or a Wi-Fi name, password or security that does not fit the rules above. The detail says which. A body that is not valid JSON, or that has an unknown field, also returns 400, as problem details without code or retryable. |
| 401 | - | The API key is missing, invalid, expired or revoked. |
| 403 | forbidden | The key lacks PROVISIONING or POLICIES_READ. |
| 404 | not_found | No policy with this policyId in the key's company. |
| 409 | unsupported | The policy cannot provision devices with a QR code: it is a VR-only policy, or the company has not finished its Android Enterprise enrollment. |
| 422 | refused | The policy was deleted. |
| 429 | rate_limited | Too many write requests for this key. The Retry-After header and the detail give the seconds to wait. retryable is true. |
| 502 | provider_error | Google did not create the enrollment token. No QR code was created. retryable is true: try again in a moment. |
Rate limits
Each request counts toward the write limit of 10 requests per minute per key, shared with the device command and policy change endpoints and the MCP write tools. A Sign in with Nomid connection that calls create_provisioning_qr_code spends the same limit, counted per connection. Requests that fail the permission checks or argument validation do not count.
MCP tool
The MCP tool create_provisioning_qr_code takes the same options, with policyId as an argument. It returns the QR code as an MCP image block, so the assistant can show it to the user without reading it.