Policy changes

Change the settings of an Android policy, create a policy as a copy of another, or roll a policy back to an earlier revision. Every change is reviewed before it is applied.

How it works

Every change takes two steps. First you propose it: the API records a proposal and returns its change list, with each setting's current and new value, the impact and when the proposal expires. Nothing changes on the policy yet. Then you apply the proposal by its proposalId, after the person responsible has reviewed the change list.

Only the API key that made a proposal can read or apply it. For an OAuth connection, only the same connection and user can. A proposal expires 10 minutes after it is made; the expiresAt field gives the exact time.

Only Android policies can be changed this way. Policies managed by Nomid cannot. Creating a policy also requires the company to be connected to Android Enterprise.

Requirements

  • An API key with the POLICIES_WRITE permission (Policy changes), combined with POLICIES_READ. Only a portal user with permission to edit policies can create a key with POLICIES_WRITE. Required by the four POST endpoints and the MCP policy change tools.
  • The company setting Policy changes, under AI agent access in the portal, must be turned on. It is off by default. While it is off, every proposal and apply call returns 403. Reading settings, revisions and proposals needs POLICIES_READ only.
  • MCP clients that connect with OAuth instead of an API key need the mcp:policies:write scope.

Endpoints

All paths are relative to the API base URL and need the X-API-Key header. Request bodies are JSON.

MethodEndpointDescription
GET/policies/{policyId}/settingsThe policy's editable settings, each with its current value, allowed values, group and whether changing it is high impact. Also returns the policy version, deviceCount and whether the policy is editable.
GET/policies/{policyId}/revisionsThe policy's saved revisions, newest first, with when and by whom each was saved. limit is optional, from 1 to 100, default 20.
GET/policy-proposals/{proposalId}A proposal made by this key, with its change list and status: PENDING, EXECUTING, SUCCEEDED, FAILED, UNCONFIRMED or EXPIRED.
POST/policies/{policyId}/changesPropose setting changes to an existing policy. Returns 201 with the proposal.
POST/policiesPropose a new policy as a copy of an existing one, with optional setting changes. Without changes it duplicates the policy. Returns 201 with the proposal.
POST/policies/{policyId}/rollbacksPropose restoring the policy to an earlier revision. Returns 201 with the proposal.
POST/policy-proposals/{proposalId}/applyApply a pending proposal. Returns 200 with the proposal and its result: policyId, displayName, version and a portal link.

Request bodies

Change settings

FieldRequiredDescription
changesYesArray of objects with setting (a setting ID) and value (one of its allowed values). Each setting at most once. Values are not case-sensitive.

Create a policy

FieldRequiredDescription
fromPolicyIdYesThe policy to copy, by its policy ID (pathName). The copy takes its apps, settings and restrictions.
displayNameYesName of the new policy as shown in the portal, at most 50 characters.
changesNoSetting changes to apply to the copy, in the same format as above. Leave it out to duplicate the policy. No device uses the new policy until you move devices to it.

Roll back a policy

FieldRequiredDescription
revisionYesThe revision number to restore, from the revisions endpoint. The policy's description, tags and group stay as they are. Its name goes back to the revision's name when they differ, and the change list shows it.

Apply a proposal

FieldRequiredDescription
confirmationFor HIGH impactRequired when the proposal has requiresConfirmation set to true: the policy's name, typed by the person approving the change. Matching ignores letter case and surrounding spaces.

The proposal

Every propose endpoint and the get and apply endpoints return the proposal. The most useful fields:

FieldDescription
proposalIdThe ID to read or apply the proposal with.
statusPENDING until applied, and EXECUTING while an apply is running: neither is final. Then SUCCEEDED or FAILED, UNCONFIRMED when an unexpected error left the outcome unknown, or EXPIRED once expiresAt has passed.
impactNORMAL or HIGH. See Impact and confirmation.
requiresConfirmationTrue for a HIGH impact proposal: apply needs the confirmation field.
fieldsThe change list. Each entry has label (the setting's name), value (the new value) and previousValue (the current value).
summaryOne sentence describing the change, including how many devices use the policy.
targetThe policy the proposal changes: kind, id and name.
expiresAtWhen the proposal expires. After that it can no longer be applied.
resultApply responses only: the policy as it is now, with policyId, displayName, version, a message and portalUrl.

Impact and confirmation

A proposal is HIGH impact when the policy has at least one device and any of these is true:

  • It is a rollback.
  • It changes a high-impact setting: apps from unknown sources, Google Play Protect, factory reset, developer options, USB data transfer or storage encryption.
  • The policy has 50 or more devices, whatever the change.

Everything else is NORMAL, including every new policy, since no device uses it yet.

Proposals are pinned to a policy version

A proposal records the policy version its change list was built on. If the policy is saved again before you apply the proposal, by anyone and from anywhere, apply fails with 409 and code stale_proposal, and nothing changes. Read the policy again, propose again and review the new change list.

Example: propose and apply

Read the policy's current settings first, to get the setting IDs and allowed values:

curl "https://api.nomid.tech/emm/api/v1/policies/p7k2m9qa4xz/settings" \
  -H "X-API-Key: $NOMID_API_KEY"
{
  "policyId": "p7k2m9qa4xz",
  "displayName": "Warehouse scanners",
  "version": 12,
  "deviceCount": 18,
  "editable": true,
  "settings": [
    {
      "id": "CAMERA",
      "label": "Camera",
      "group": "RESTRICTIONS",
      "value": "ALLOW",
      "allowedValues": ["ALLOW", "BLOCK"],
      "highImpact": false
    }
  ]
}

Propose blocking the camera and USB data transfer:

curl -X POST "https://api.nomid.tech/emm/api/v1/policies/p7k2m9qa4xz/changes" \
  -H "X-API-Key: $NOMID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"changes": [{"setting": "CAMERA", "value": "BLOCK"}, {"setting": "USB_DATA_ACCESS", "value": "BLOCK"}]}'
HTTP/1.1 201 Created

{
  "capability": "update_policy_settings",
  "status": "PENDING",
  "title": "Edit policy settings",
  "summary": "Change 2 settings of policy \"Warehouse scanners\". 18 devices apply it on their next sync. High impact: type the policy's name to confirm.",
  "target": { "kind": "policy", "id": "p7k2m9qa4xz", "name": "Warehouse scanners" },
  "fields": [
    { "label": "Camera", "value": "BLOCK", "previousValue": "ALLOW" },
    { "label": "USB data transfer", "value": "BLOCK", "previousValue": "ALLOW" }
  ],
  "expiresAt": "2026-10-09T14:40:00Z",
  "impact": "HIGH",
  "proposalId": "prp_7GQv2LkR9sTn4WxY8bZcA1dE",
  "requiresConfirmation": true
}

Abridged response. USB data transfer is a high-impact setting and the policy has devices, so the proposal is HIGH impact and requires confirmation. Show the change list to the person responsible before applying it.

Once they approve and type the policy's name, apply the proposal with what they typed:

curl -X POST "https://api.nomid.tech/emm/api/v1/policy-proposals/prp_7GQv2LkR9sTn4WxY8bZcA1dE/apply" \
  -H "X-API-Key: $NOMID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirmation": "Warehouse scanners"}'
HTTP/1.1 200 OK

{
  "capability": "update_policy_settings",
  "status": "SUCCEEDED",
  "impact": "HIGH",
  "proposalId": "prp_7GQv2LkR9sTn4WxY8bZcA1dE",
  "requiresConfirmation": true,
  "result": {
    "policyId": "p7k2m9qa4xz",
    "displayName": "Warehouse scanners",
    "version": 13,
    "message": "Policy settings changed. Devices on the policy apply them on their next sync."
  }
}

Abridged response. Devices on the policy apply the new settings on their next sync.

When the policy changed in the meantime

HTTP/1.1 409 Conflict
Content-Type: application/problem+json

{
  "type": "about:blank",
  "title": "Conflict",
  "status": 409,
  "detail": "Policy \"Warehouse scanners\" was changed after this proposal was made. Nothing was applied: read it again and propose again.",
  "instance": "/emm/api/v1/policy-proposals/prp_7GQv2LkR9sTn4WxY8bZcA1dE/apply",
  "code": "stale_proposal",
  "retryable": false
}

Editable settings

These are the setting IDs accepted in changes, with their allowed values. The label is the name the API returns in the change list.

When reading settings, a value can also be NOT_SET (left to the device or Google's default) or CUSTOM (a value set in the portal that these values cannot express). Neither can be sent, but both can be replaced with an allowed value.

SettingLabelGroupAllowed valuesHigh impact
PLAY_STORE_MODE Play Store mode APPS BLACKLIST, WHITELIST No
DEFAULT_PERMISSION_POLICY Default runtime permission policy APPS DENY, PROMPT No
APP_AUTO_UPDATE_POLICY App auto-update policy APPS ALWAYS, CHOICE_TO_THE_USER, NEVER, WIFI No
UNTRUSTED_APPS_POLICY Apps from unknown sources SECURITY ALLOW_INSTALL_IN_PERSONAL_PROFILE_ONLY, DISALLOW_INSTALL Yes
PLAY_PROTECT Google Play Protect app verification SECURITY ENABLED, USER_CHOICE Yes
SCREEN_CAPTURE Screen capture RESTRICTIONS ALLOW, BLOCK No
CAMERA Camera RESTRICTIONS ALLOW, BLOCK No
FACTORY_RESET Factory reset from Settings RESTRICTIONS ALLOW, BLOCK Yes
UNINSTALL_APPS Uninstalling apps RESTRICTIONS ALLOW, BLOCK No
ACCOUNT_MODIFICATION Adding or removing accounts RESTRICTIONS ALLOW, BLOCK No
ADD_USER Adding users RESTRICTIONS ALLOW, BLOCK No
REMOVE_USER Removing users RESTRICTIONS ALLOW, BLOCK No
DEVELOPER_SETTINGS Developer options and USB debugging SECURITY ALLOW, BLOCK Yes
USB_DATA_ACCESS USB data transfer CONNECTIVITY ALLOW, BLOCK Yes
LOCATION_MODE Location LOCATION DISABLED, ENFORCED, USER_CHOICE No
LOCATION_SHARING Sharing location LOCATION ALLOW, BLOCK No
OUTGOING_CALLS Outgoing calls RESTRICTIONS ALLOW, BLOCK No
SMS SMS RESTRICTIONS ALLOW, BLOCK No
BLUETOOTH Bluetooth CONNECTIVITY ALLOW, BLOCK No
DATA_ROAMING Data roaming CONNECTIVITY ALLOW, BLOCK No
NETWORK_RESET Network settings reset CONNECTIVITY ALLOW, BLOCK No
VPN_CONFIGURATION Configuring VPNs CONNECTIVITY ALLOW, BLOCK No
CONFIGURE_WIFI Configuring Wi-Fi networks CONNECTIVITY ALLOW, BLOCK No
WIFI_DIRECT Wi-Fi Direct CONNECTIVITY ALLOW, BLOCK No
TETHERING Tethering and hotspot CONNECTIVITY ALLOW, BLOCK No
WIFI_STATE Wi-Fi on or off CONNECTIVITY DISABLED, ENABLED, USER_CHOICE No
AIRPLANE_MODE Airplane mode CONNECTIVITY DISABLED, USER_CHOICE No
MINIMUM_WIFI_SECURITY Minimum Wi-Fi security CONNECTIVITY OPEN_NETWORK, PERSONAL_NETWORK No
AUTO_DATE_TIME Automatic date, time and time zone RESTRICTIONS ENFORCED, USER_CHOICE No
ENCRYPTION_POLICY Storage encryption SECURITY ENABLED_WITHOUT_PASSWORD, ENABLED_WITH_PASSWORD, UNSPECIFIED Yes
APPLICATION_REPORT_LEVEL Installed apps reporting REPORTING DISABLED, INSTALLED_AND_REMOVED_APPS, INSTALLED_APPS No
REPORT_DEVICE_SETTINGS Device settings reporting REPORTING DISABLED, ENABLED No
REPORT_DISPLAY_INFO Display information reporting REPORTING DISABLED, ENABLED No
REPORT_HARDWARE_STATUS Hardware status reporting REPORTING DISABLED, ENABLED No
REPORT_MEMORY_INFO Memory information reporting REPORTING DISABLED, ENABLED No
REPORT_NETWORK_INFO Network information reporting REPORTING DISABLED, ENABLED No
REPORT_POWER_EVENTS Power events reporting REPORTING DISABLED, ENABLED No
REPORT_SOFTWARE_INFO Software information reporting REPORTING DISABLED, ENABLED No
REPORT_SYSTEM_PROPERTIES System properties reporting REPORTING DISABLED, ENABLED No

MCP tools

The MCP server exposes the same operations as tools. They take the same arguments as the REST endpoints, with policyId and proposalId as arguments instead of path segments.

ToolPermissionDescription
get_policy_settingsPOLICIES_READRead a policy's editable settings with their current and allowed values.
list_policy_revisionsPOLICIES_READList a policy's saved revisions, newest first.
get_policy_proposalPOLICIES_READRead a proposal made by the same key or connection, with its status.
update_policy_settingsPOLICIES_WRITE + POLICIES_READPropose setting changes to an existing policy.
create_policyPOLICIES_WRITE + POLICIES_READPropose a new policy as a copy of an existing one, with optional setting changes.
rollback_policyPOLICIES_WRITE + POLICIES_READPropose restoring a policy to an earlier revision.
apply_policy_proposalPOLICIES_WRITE + POLICIES_READApply a pending proposal by its proposalId, with confirmation when it is HIGH impact.

An assistant must show the user the change list and get their explicit approval in the conversation before it calls apply_policy_proposal. For a HIGH impact proposal it must ask the user to type the policy's name and pass exactly what they typed.

MCP server

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 missing or invalid key gets the shared 401 response, a small JSON object with error, message and status. MCP tools return the same code and message as a tool error.

StatusCodeMeaning
400invalid_argumentsThe body is invalid: an unknown setting, a value that is not allowed, a setting listed twice, or a missing field. The detail says which.
403forbiddenThe key lacks POLICIES_WRITE or POLICIES_READ, or the company setting Policy changes is off.
404not_foundNo such policy or revision visible to this key, or no proposal with this ID made by this key.
409unsupportedThe policy cannot be changed this way: it is not an Android policy, or it is managed by Nomid.
409not_pendingThe proposal is no longer PENDING. Read it to see its status: if it is SUCCEEDED, the change is made. If it is EXECUTING, read it again until it finishes. If it is UNCONFIRMED, read the policy's settings before proposing again. If it is FAILED or EXPIRED, propose again.
409stale_proposalThe policy was saved after the proposal was made. Nothing was applied. Read the policy again and propose again.
422refusedThe request cannot be carried out: nothing would change, the company is not connected to Android Enterprise, or the new policy name cannot be used.
422confirmation_requiredThe proposal is HIGH impact and confirmation is missing or does not match the policy's name. Nothing was applied, and the proposal stays pending until it expires.
429rate_limitedToo many write requests for this key. The Retry-After header and the detail give the seconds to wait. retryable is true.
500internal_errorUnexpected error. Read the proposal before retrying: if its status is FAILED, propose again. If it is UNCONFIRMED, read the policy's settings to see whether the change was made before proposing it again.

Rate limits

Each propose and apply call counts toward the per-key write limit of 10 requests per minute, shared with the device command endpoints and the MCP write tools. Read calls do not count toward it.

Revisions and rollback

Every applied change saves a new revision of the policy. List revisions to see who changed the policy and when, and propose a rollback to undo a change.

Choose Your Time

Loading...
Open booking calendar