POST

/api/im/permissions/request

Request approval for a high-risk operation

Daemon/agent-facing endpoint that creates an approval request for a capability-scoped

operation. The risk level is computed server-side from capability + context via

calculateRiskLevel() (see src/im/services/permission.service.ts).

Behaviour:

Low risk (level: "low" or "medium") → auto-approved, returns 200 with

{ approved: true, riskLevel, message }. No database row is created.

High / critical risk → creates a pending im_approval_requests row with a

5-minute TTL (override via ttlMs), sends an APNs/FCM push to the owning user,

and returns 202 with { requestId, expiresAt, riskLevel, message }. The caller

should poll GET /api/im/permissions/{id} or listen on SSE until resolution.

Idempotent retry — if a pending request with the same `(userId, capability,

operation) tuple already exists, the existing requestId/expiresAt` is returned

with message: "Approval request already exists (idempotent retry)".

Spec reference: docs/version190/16-user-journeys.md §16.3 "Remote approval".

No code sample available for this language

Parameters

FieldTypeReqDefaultDescription
Idempotency-KeystringNOptional client-supplied idempotency hint (server also dedupes on the capability/operation tuple).

Request Body

FieldTypeReqDefaultDescription
capabilitystringYCapability ID (e.g. `shell.execute`, `file.write`, `network.request`).
operationstringYHuman-readable one-liner shown to the user in the push notification.
contextobjectNOptional structured context (path, url, fileSize, sudo, …). Used for risk scoring.
ttlMsintegerNOverride TTL in milliseconds. Default 300000 (5 min).

Response Example

{
  "ok": true,
  "data": {
    "approved": true,
    "riskLevel": {
      "level": "low",
      "score": 10,
      "factors": []
    },
    "message": "Low risk operation, auto-approved"
  }
}

Try it out

Sign in to use your API key
Request Body
Response
Click Execute to test