Skip to content

REST API

Base path /api/v1. Authenticate with Authorization: Bearer <token>, using an API token (vsat_...) or the token returned by login. Bodies are JSON; unknown fields are rejected.

Minimum role in brackets.

Auth

Method Path
POST /auth/login {email, password} -> {user, token}. 401 for a wrong email or password; 403 when the password is right but password sign-in is off for that account (VS_PASSWORD_LOGIN)
POST /auth/logout clears the session cookie
GET /auth/providers unauthenticated: {password, passwordRestricted, oidc: {enabled, name}} for the sign-in page. password is false when password sign-in is off; passwordRestricted is true when only break-glass accounts may still use it
GET /auth/oidc/start?next= unauthenticated: redirects to the identity provider; next is a path on this site. See sso.md
GET /auth/oidc/callback unauthenticated: the provider's redirect target; sets the session cookie and redirects to next, or to /login?sso_error=<code>
GET /auth/me [auditor] current user; sso and hasPassword tell how they sign in
POST /auth/password [auditor] {current, new}
GET, POST /api-tokens [auditor] list or create ({name, expiresInDays}); the token is returned once
DELETE /api-tokens/{id} [auditor]

Devices and commands

Method Path
GET /devices?platform=&ownership=&status=&group=&tag=&compliant=&q=&limit=&offset= [auditor] compliant is true, false or unknown (never evaluated)
GET /devices/{id} [auditor] device, groups, effective policy
PATCH /devices/{id} [operator] {name, assignee}; ownership needs admin. Changing ownership or assignee re-evaluates smart groups and re-pushes policy. Making a device personal removes its serial and inventory and cancels pending commands not allowed on personal devices
DELETE /devices/{id} [admin] deletes the record only; retire the device first
GET /devices/{id}/commands?limit= [auditor] newest first, up to 500
POST /devices/{id}/commands [operator] {type, params}; returns 202 with the queued command, 403 if not permitted, 409 if the device's ownership changed meanwhile
POST /commands/{id}/cancel [operator]
GET /commands/catalogue [auditor] command types, roles, platforms, BYOD eligibility
GET /devices/{id}/inventory?kind=app\|service\|profile&q=&limit=&offset= [auditor] installed apps, services or profiles, with counts, supported and a BYOD note
GET /inventory/software?kind=&q=&limit=&offset= [auditor] software across the fleet with device counts and versions
GET /inventory/software/devices?kind=&identifier=&name=&version= [auditor] devices that have one piece of software

Command params: message, phone, pin (macOS, 6 digits), appId, url, hash and version (Windows MSI), script (Linux), preserveDataPlan. message and phone are shown on Apple devices only.

Parameters are checked against the target platform when the command is sent, and a command that cannot be delivered is refused with 403:

Command Requires
install_app, remove_app (any platform) appId or url
install_app on Windows appId (MSI product code), url, hash and version
remove_app on Windows appId (MSI product code)
remove_app on Apple appId (bundle ID)
install_app, remove_app on Linux appId, a package name
run_script script
curl -X POST https://mdm.example.com/api/v1/devices/$ID/commands \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"type":"lock","params":{"message":"Please return to IT"}}'

Enrollment

Method Path
GET /enrollment-tokens [operator]
POST /enrollment-tokens [operator] {platform, ownership, assignee, groupId, maxUses, expiresInHours} -> {token, meta, instructions}
DELETE /enrollment-tokens/{id} [operator] revoke
GET /enrollment-tokens/{id}/qr.png [operator] Android provisioning QR

Policies, groups and blueprints

Method Path
GET /policies [auditor] Wi-Fi passwords masked
POST /policies [admin] {name, description, priority, document, groupIds, deviceIds}
GET, PUT, DELETE /policies/{id} [auditor] / [admin]
GET /groups?kind=static\|smart [auditor]
POST /groups [admin] {name, description, kind, rules}
GET, PUT, DELETE /groups/{id} [auditor] / [admin]; GET includes member IDs
POST /groups/{id}/devices [operator] {deviceId}; static groups only (409 for smart)
DELETE /groups/{id}/devices/{deviceId} [operator] static groups only
POST /groups/preview [auditor] body: a rule set; returns matching devices without saving
GET /groups/schema [auditor] rule fields and operators
PUT /devices/{id}/tags [operator] {tags: [...]}; re-evaluates smart groups immediately
GET /blueprints [auditor]
POST /blueprints [admin] {name, description, priority, groupIds, spec}
GET, PUT, DELETE /blueprints/{id} [auditor] / [admin]

Saving a policy or blueprint, or a membership change, queues apply_policy for every affected device, and devices newly in scope of a blueprint run its onboarding steps. Formats are in manifests.md.

Declarative apply

Method Path
POST /apply?dryRun=&prune=&owner= [admin] YAML or JSON manifest; returns {dryRun, changes: [{kind, name, action}]}. 400 with problems if invalid (nothing written)
GET /export?format=yaml\|json [admin] every group, policy and blueprint as manifests, secrets included

vsctl wraps these; see manifests.md.

Webhooks

All [admin]. See webhooks.md for events, the request format and signature verification.

Method Path
GET /webhooks every webhook with its lastDelivery
GET /webhooks/events subscribable events with descriptions
POST /webhooks {name, url, events, enabled} -> {webhook, secret}; the signing secret is returned only here. 400 with problems if invalid
GET, PUT, DELETE /webhooks/{id} PUT takes the same body as POST and keeps the secret
POST /webhooks/{id}/rotate-secret -> {secret}; requests are signed with it from now on
POST /webhooks/{id}/test queues a webhook.test event for this webhook only; 202 with the delivery
GET /webhooks/{id}/deliveries?before=&limit= newest first, with status, attempts, response code, last error and the event payload
POST /webhooks/{id}/deliveries/{deliveryId}/retry send a pending or failed delivery again now; 409 if it was delivered or is being sent

Platforms

All [admin], except the Android sign-up callback. Credentials set here are stored encrypted; those set by environment variables cannot be changed here.

Method Path
GET /platforms state of Apple, Google, Android, ChromeOS, Windows and Linux, with the public URL
POST /platforms/apple/csr -> {csr}: a push certificate signing request; its key stays on the server
PUT, DELETE /platforms/apple PUT {certificate, der, privateKey, topic}: the push certificate from Apple (PEM, or base64 DER with der: true); privateKey is optional when the CSR came from the server, topic when it can be read from the certificate
PUT, DELETE /platforms/google PUT {credentials, projectId}: a service account key (JSON)
POST /platforms/android/signup -> {signupUrl}: starts Android Enterprise sign-up
GET /platforms/android/callback unauthenticated: Google's redirect after sign-up
PUT, DELETE /platforms/android PUT {enterprise}: use an existing enterprise
PUT, DELETE /platforms/chromeos PUT {adminSubject, customerId}

Administration

Method Path
GET, POST /users [admin]
PATCH, DELETE /users/{id} [admin] {name, role, disabled, password}
GET /audit?before=&limit= [auditor]
GET /stats [auditor] includes software: {apps, devicesReporting}
GET /info [auditor] version, enabled platforms, CA fingerprint

Unauthenticated: GET /healthz, GET /readyz, GET /mdm/ca.pem.