SYNERGY
Chapters

15.6Changes that wait for approval

Last updated

When it happens

A person is never queued. An API key is a machine credential, so each change it makes to Active Directory is checked against:

  • the key's supervision level — Unsupervised (Act without asking in the console), Guarded (Ask before destructive and security changes, the default) or Supervised (Ask before every change);
  • whether the target is a protected object (adminCount=1) or lies in a protected container set on the connection;
  • whether an administrator has pinned the operation to always ask agents — unless the key has Skip the always-ask wait turned on;
  • the volume brake, which holds everything the key does once it has made too many destructive changes in a rolling hour (20 by default).

Under the default Guarded level, these wait for a person: deletes; removing group members or memberships (including the clear routes); revoking a delegation; changes to access control lists, delegations, deletion protection, SPNs, password policies and password-retrieval principals; writes to privileged attributes; restoring deleted objects; and enabling or disabling all the accounts in an organizational unit. Ordinary creates, updates, enables, disables, unlocks, password resets, moves, renames and member additions run straight away. Chapter 10, Operation policies, attribute policies and agent supervision, explains every rule and how administrators set them.

The 202 response

When a change has to wait, the gateway records it, does not carry it out, and answers 202 Accepted:

http
HTTP/1.1 202 Accepted
Location: /api/operations/6a1f0c2e-8d1b-4b7a-9f62-3c0e2d7a91b4
Retry-After: 30
Content-Type: application/json

{
  "operationId": "6a1f0c2e-8d1b-4b7a-9f62-3c0e2d7a91b4",
  "code": "Operation.ApprovalRequired",
  "status": "WaitingToApprove",
  "displayName": "Delete User",
  "message": "The operation 'Delete User' is waiting for a person to approve it. This agent asks before destructive and security changes.",
  "expiresAt": "2026-10-06T09:15:00",
  "reason": "Level",
  "reasonDetail": "This agent asks before destructive and security changes.",
  "pollUrl": "/api/operations/6a1f0c2e-8d1b-4b7a-9f62-3c0e2d7a91b4"
}
FieldMeaning
operationIdThe handle to poll.
codeAlways Operation.ApprovalRequired.
statusWaitingToApprove.
displayNameThe operation's name, for example "Create User".
messageA sentence for people, naming the operation and why it waits.
expiresAtWhen the request lapses if nobody decides it: 72 hours after it was queued. The time is UTC, even when it carries no Z.
reasonWhy it waits: ProtectedTarget, Pinned, Volume or Level.
reasonDetailThe same reason in plain text.
pollUrlWhere to poll, including any application path. Also in the Location header.
Important: The body of a 202 is a handle, not the object you asked for. The object does not exist yet. Never treat a 202 as success, and never read operationId as the new object's ID.

Use reason to decide what to tell your users. Volume means "the integration has been busy; new requests wait until its count of recent destructive changes falls back under the threshold". ProtectedTarget means "a person must look at this, whatever the integration's settings". Pinned means an administrator wants a person to approve this operation whenever an agent or integration asks for it. Level means the key's own supervision level asks about this kind of change.

A change that would wait and carries a password is refused instead, with 403 Agent.ApprovalImpossible: storing a password until somebody decides would leave it in clear text. In practice this means no API key can set the password of a protected account, and a Supervised key cannot reset passwords at all.

Polling for the outcome

Poll the operation with GET /operations/{id} (or the pollUrl), waiting at least the Retry-After interval between requests. It needs operations:waiting:list, and returns only operations made by the same person; anyone else's answer 404.

json
{
  "id": "6a1f0c2e-8d1b-4b7a-9f62-3c0e2d7a91b4",
  "displayName": "Create User",
  "status": "Success",
  "gateReason": "This agent asks before every change it makes.",
  "apiKeyName": "hr-sync",
  "expiresAt": "2026-10-06T09:15:00",
  "decidedAt": "2026-10-03T10:02:41",
  "approvedByEmail": "approver@example.com",
  "result": {
    "entityId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
    "distinguishedName": "CN=Jane Doe,OU=Staff,DC=example,DC=com",
    "objectClass": "user",
    "action": 1
  }
}

The record carries more fields than shown, including gateReason (why it waited, in plain text), impactSummary (when present, what the change would affect), apiKeyName, errorCode and errorMessage. Fields with no value are left out of the response.

The operation's status is one of:

StatusMeaningFinal
WaitingToApproveStill waiting for a decision.No
SuccessApproved and carried out.Yes
RejectedAn approver rejected it.Yes
FailedApproved, but carrying it out failed (see errorCode and errorMessage), or it was approved after it had expired.Yes
CancelledThe requester withdrew it in the web console.Yes

Stop polling when the status is final, or when expiresAt has passed: approvers no longer see an expired request, so nobody can decide it any more, even though its status may still read WaitingToApprove. Submit the change again if it is still needed.

The result block is how you learn what the operation produced. It holds entityId (the object's objectGUID — for a create, the new object), distinguishedName, objectClass and action — a number: 1 create, 2 update, 3 delete, 0 anything else. It is absent while the operation is waiting, and for operations that never reached a directory object. Its presence is the completion signal.

Approving and rejecting are deliberately not available to machine credentials: an API key can never approve an operation, not even one its owner could approve in the console. A person decides in the web console, as described in Chapter 9, Operations log, approvals and waiting operations.

GET /operations/waiting lists your own operations that are still waiting (paged with page and limit, 25 per page by default and at most 100, with an optional search on the operation name). GET /operations/waiting/decided lists your requests that were decided recently.

Withdrawing a waiting request is done in the web console only, under My Waiting Operations; there is no machine route for it.

Last updated

Did this page help you?