ArborXR MDM API v2 to v3 Upgrade Guide
This guide covers the changes between the ArborXR MDM API v2 and v3, helping you update your integrations.
Base URL Change
| Version | Base URL |
|---|---|
| v2 | https://api.xrdm.app/api/v2 |
| v3 | https://api.xrdm.app/api/v3 |
Authentication Change
Important: API tokens have changed from organization-scoped to user-scoped in v3.
| Aspect | v2 | v3 |
|---|---|---|
| Token scope | Organization-wide | User-specific |
| Token permissions | Based on organization | Based on user's roles and permissions within an organization |
| v2 tokens in v3 | N/A | Not supported |
Action required: You must generate new API tokens for your v3 integrations. Existing v2 tokens will not work with v3 endpoints.
To generate a new token:
- Navigate to your organization's settings
- Go to the Access Tokens section
- Create a new token for v3 API access
Removed Endpoints
The following deprecated v2 endpoints have been removed in v3:
Device Groups (use /groups instead)
| Removed Endpoint | Replacement |
|---|---|
GET /device-groups | GET /groups |
GET /device-groups/{groupId} | GET /groups/{groupId} |
POST /device-groups | POST /groups |
PUT /device-groups/{groupId} | PUT /groups/{groupId} |
DELETE /device-groups/{groupId} | DELETE /groups/{groupId} |
GET /device-groups/{groupId}/release-channels | GET /groups/{groupId}/release-channels |
POST /device-groups/{groupId}/release-channels | POST /groups/{groupId}/release-channels |
DELETE /device-groups/{groupId}/release-channels | DELETE /groups/{groupId}/release-channels/{releaseChannelId} |
User Invites
| Removed Endpoint | Replacement |
|---|---|
POST /invite-user | POST /user-invites |
Roles
| Removed Endpoint | Replacement |
|---|---|
GET /organization-roles | GET /roles |
GET /organization-roles/{roleId} | GET /roles/{roleId} |
GET /group-roles | GET /roles |
GET /group-roles/{roleId} | GET /roles/{roleId} |
Changed Endpoints
Unshare Release Channel
The method for unsharing a release channel has changed from DELETE to POST with a dedicated unshare endpoint.
v2:
DELETE /apps/{appId}/release-channels/{releaseChannelId}/share
v3:
POST /apps/{appId}/release-channels/{releaseChannelId}/unshare
Remove Release Channel from Group/Device
Release channel removal now uses path-based identification instead of body-based.
v2:
DELETE /groups/{groupId}/release-channels
Body: { "releaseChannelId": "..." }
DELETE /devices/{deviceId}/release-channels
Body: { "releaseChannelId": "..." }
v3:
DELETE /groups/{groupId}/release-channels/{releaseChannelId}
DELETE /devices/{deviceId}/release-channels/{releaseChannelId}
Remove File from Group/Device
File removal now uses path-based identification instead of body-based.
v2:
DELETE /groups/{groupId}/files
Body: { "fileId": "..." }
DELETE /devices/{deviceId}/files
Body: { "fileId": "..." }
v3:
DELETE /groups/{groupId}/files/{fileId}
DELETE /devices/{deviceId}/files/{fileId}
Remove Video from Group/Device
Video removal now uses path-based identification instead of body-based.
v2:
DELETE /groups/{groupId}/videos
Body: { "videoId": "..." }
DELETE /devices/{deviceId}/videos
Body: { "videoId": "..." }
v3:
DELETE /groups/{groupId}/videos/{videoId}
DELETE /devices/{deviceId}/videos/{videoId}
Migrate Device to Organization
The authentication mechanism for migrating devices between organizations has changed significantly.
v2:
POST /devices/{deviceId}/migrate/{organizationSlug}
Body: { "targetOrganizationToken": "43|gC1ajKELCmETw...", "groupId": "..." }
Required a targetOrganizationToken - an API token for the target organization with MDM API ability.
v3:
POST /devices/{deviceId}/migrate/{organizationSlug}
Body: { "groupId": "..." }
The targetOrganizationToken field has been removed. Instead, the authenticated user must:
- Belong to both the source and target organizations
- Have
DEVICE__MIGRATEpermission in the source organization - Have
ENROLLMENT__ENROLL_DEVICEpermission in the target organization - Have
GROUP__ADD_REMOVE_DEVICESpermission on the target group (ifgroupIdis specified)
Device Models
The GET /device-models endpoint behavior has changed:
v2: Required full=true query parameter to get all supported models plus enrolled unsupported models.
v3: Returns all supported models plus enrolled unsupported models by default (no query parameter needed).
New Endpoints
Current User
Get information about the currently authenticated user:
GET /current-user
Users Management
Full CRUD operations for managing users:
GET /users # List users
POST /users # Create user
GET /users/{userId} # Get user
PUT /users/{userId} # Update user
DELETE /users/{userId} # Delete user
Unified Roles
Roles are now unified under a single endpoint:
GET /roles # List all roles
GET /roles/{roleId} # Get role details
File Deletion
Delete files directly:
DELETE /files/{fileId}
Video Deletion
Delete videos directly:
DELETE /videos/{videoId}
Response Body Changes (Schema Updates)
Renamed Fields
The following fields have been renamed across multiple resources for consistency:
| Resource | v2 Field | v3 Field |
|---|---|---|
| Device | title | name |
| Group | title | name |
| App | title | name |
| Role | title | name |
Device Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Name field | title | name |
| Group reference | deviceGroup (string, deprecated) | Removed (use group object) |
| Model reference | model (string) | deviceModel (expanded object with id, name, manufacturer, isSupported) |
| Storage free | storageSpaceFreeGb (float, GB) | storageSpaceFreeBytes (int64, bytes) |
| Storage total | storageSpaceTotalGb (float, GB) | storageSpaceTotalBytes (int64, bytes) |
| Custom fields | Inline object | $ref to CustomField schema |
| Timestamps | Not always present | createdAt, updatedAt always included |
| Organization | Not included | organization object included |
Group Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Name field | title | name |
| Parent reference | parentId (UUID) + parent (expanded Group, if set) | parent only (expanded Group object, nullable) - parentId removed |
| Timestamps | Not included | createdAt, updatedAt always included |
User Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Organization role | organizationRole object | Removed |
| Group roles | groupRoles array of {group, role} | Removed |
| Access controls | Not included | accessControls array (unified structure with role + protectable) |
The accessControls array in v3 contains objects with:
id: Access control IDuserId: User IDrole: Role objectprotectable: Object withidandtype(organization, group, or device)createdAt,updatedAt: Timestamps
Role Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Name field | title | name |
| Type enum values | "Organization", "Group" (capitalized) | "organization", "group" (lowercase) |
| Timestamps | Not included | createdAt, updatedAt always included |
File Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Human-readable size | size (e.g., "101 MB") | Removed (use sizeBytes) |
| Filename | name | filename |
| Checksum | sha512 (string) | checksum object with algorithm and value |
| Device Status | Not included | deviceStatuses array of objects with deviceId, status, statusTimestamp |
| Device IDs | deviceIds array | Removed (use deviceStatuses) |
| Timestamp | added | createdAt, updatedAt |
Video Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Human-readable size | size (e.g., "250 MB") | Removed (use sizeBytes) |
| Content name | Not included | name |
| Filename | name | filename |
| Checksum | Not included | checksum object with algorithm and value |
| Device Status | Not included | deviceStatuses array of objects with deviceId, status, statusTimestamp |
| Device IDs | deviceIds array | Removed (use deviceStatuses) |
| Timestamp | added | createdAt, updatedAt |
App Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Name field | title | name |
| Timestamps | Not included | createdAt, updatedAt always included |
ReleaseChannel Schema Changes
| Change | v2 | v3 |
|---|---|---|
| ID format | String (no format specified) | UUID format |
| App reference | Not included | app object with full App schema |
| Version | Conditionally loaded | version always included with full AppVersion schema |
| Device Status | Not included | deviceStatuses array (see below) |
| Device IDs | deviceIds array | Removed (use deviceStatuses) |
| Timestamps | Not included | createdAt, updatedAt always included |
The deviceStatuses array contains objects with:
deviceId: Device UUIDtargetVersion: Target version name from the release channelinstalledVersion: Currently installed version name on the devicestatus: Status enum valuestatusTimestamp: ISO8601 timestamp with millisecond precision
DeviceModel Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Support status | Not included | isSupported boolean |
| Timestamps | Not included | createdAt, updatedAt always included |
Organization Schema Changes
| Change | v2 | v3 |
|---|---|---|
| Timestamps | Not included | createdAt, updatedAt always included |
AuditLog Schema Changes
| Change | v2 | v3 |
|---|---|---|
| User info | user object with id, email, firstName, lastName | Flattened to userId and userEmail (no name fields) |
| Resource fields | resource | Split into resourceId, resourceType, and resourceName |
| IP Address | ipAddress | userIpAddress |
| Type field | String | Enum (string): created, read, updated, deleted, remote_action, exported |
| Timestamps | createdAt only | createdAt, updatedAt always included |
New Checksum Object Structure
v3 introduces a structured checksum object instead of plain hash strings:
{
"checksum": {
"algorithm": "SHA-512",
"value": "3a7bd3e2360a3d29eea4a5c13c6b1b9b..."
}
}
Files and Videos use SHA-512 (or MD5 for legacy content). AppBuilds use SHA-256.
Timestamp Precision
All datetime fields in v3 now include millisecond precision in ISO8601 format:
- v2:
2024-01-15T14:30:00Z - v3:
2024-01-15T14:30:00.123Z
Request Body Changes
Update Device Request
| Change | v2 | v3 |
|---|---|---|
| Name field | title | name |
| Group ID field | deviceGroupId | groupId |
Create/Update Group Request
| Change | v2 | v3 |
|---|---|---|
parentId required | Optional (omit for root-level) | Required (pass null for root-level) |
Create User Request
The user creation request has been significantly restructured:
v2:
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"organizationRoleId": "...", // OR groupRoleId + groupIds
"groupRoleId": "...",
"groupIds": ["..."]
}
v3:
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"defaultRoleId": "...", // Required - works for both org and group roles
"groupRoles": [
// Optional - for additional group roles
{
"roleId": "...",
"groupId": "..."
}
]
}
Key changes:
organizationRoleId→defaultRoleId(required for ALL users, unified for both organization and group role types)groupRoleId+groupIds→groupRolesarray with explicit role-group pairs (optional)
Update User Request
v2:
{
"firstName": "John",
"lastName": "Doe",
"organizationRoleId": "...",
"groupRoleId": "...",
"groupIds": ["..."]
}
v3:
{
"firstName": "John",
"lastName": "Doe",
"defaultRoleId": "...",
"groupRoles": [
{
"roleId": "...",
"groupId": "...",
"action": "attach" // or "detach"
}
]
}
Key changes:
organizationRoleId→defaultRoleIdgroupRoleId+groupIds→groupRolesarray with explicit attach/detach actions
The groupRoles array requires each entry to have:
roleId: UUID of the role (required)groupId: UUID of the group (required)action: Must be either"attach"or"detach"(required, no default)
Update App Request
| Change | v2 | v3 |
|---|---|---|
| Name field | title | name |
Migrate Device to Organization Request
| Change | v2 | v3 |
|---|---|---|
targetOrganizationToken | Required (API token for target org) | Removed (user must belong to target org) |
groupId | Optional | Optional (requires group permission if specified) |
Error Response Improvements
V3 API endpoints now consistently return standardized error responses:
400 Bad Request- Invalid request parameters401 Unauthorized- Missing or invalid authentication403 Forbidden- Insufficient permissions404 Not Found- Resource not found422 Unprocessable Entity- Validation errors500 Internal Server Error- Server-side errors
Upgrade Checklist
Endpoint Changes
- Generate new user-scoped API tokens (v2 organization tokens are not compatible with v3)
- Update base URL from
/api/v2to/api/v3 - Replace
/device-groupsroutes with/groups - Replace
/invite-userwith/user-invites - Replace
/organization-rolesand/group-roleswith/roles - Update release channel unshare from
DELETE .../sharetoPOST .../unshare - Update group/device release channel removal to use path-based DELETE
- Update group/device file removal to use path-based DELETE
- Update group/device video removal to use path-based DELETE
- Remove
full=trueparameter from/device-modelscalls (if used) - Update device migration to remove
targetOrganizationTokenand ensure user belongs to target organization with required permissions
Response Body Changes
- Update code parsing
titlefield to usename(Device, Group, App, Role) - Update Device parsing for
storageSpaceFreeBytes/storageSpaceTotalBytes(was GB, now bytes) - Update Device parsing for
deviceModelobject (wasmodelstring) - Update Group parsing for
parentobject (parentIdremoved, onlyparentremains) - Update User parsing for
accessControlsarray (replacesorganizationRoleandgroupRoles) - Update Role parsing for lowercase
typevalues (organization/group) - Update File/Video parsing for
checksumobject (replacessha512) - Update File/Video parsing for
createdAt/updatedAt(replacesadded) - Handle new
namefield in Video responses (content display name, separate fromfilename) - Update AuditLog parsing:
userobject flattened touserId/userEmail,resourcesplit intoresourceId/resourceType/resourceName,ipAddressrenamed touserIpAddress - Handle new ReleaseChannel fields (
appobject,versionalways included,deviceStatuseswith extended fields) - Update timestamp parsing for millisecond precision (e.g.,
2024-01-15T14:30:00.123Z) - Handle new
createdAt/updatedAttimestamps on all resources
Request Body Changes
- Update Device update requests to use
nameinstead oftitle - Update Device update requests to use
groupIdinstead ofdeviceGroupId - Update App update requests to use
nameinstead oftitle - Update Group create/update requests to always include
parentId(usenullfor root-level) - Ensure
defaultRoleIdis always provided in user creation requests (now required for ALL users) - Update User create requests to use
defaultRoleIdandgroupRolesarray - Update User update requests to use
defaultRoleIdandgroupRoleswith requiredactionfield ("attach"or"detach") - Update device migration requests to remove
targetOrganizationToken(authentication now based on user membership)
Error Handling
- Update error handling to account for consistent 400/500 responses