/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".
Parameters
| Field | Type | Req | Default | Description |
|---|---|---|---|---|
| Idempotency-Key | string | N | — | Optional client-supplied idempotency hint (server also dedupes on the capability/operation tuple). |
Request Body
| Field | Type | Req | Default | Description |
|---|---|---|---|---|
| capability | string | Y | — | Capability ID (e.g. `shell.execute`, `file.write`, `network.request`). |
| operation | string | Y | — | Human-readable one-liner shown to the user in the push notification. |
| context | object | N | — | Optional structured context (path, url, fileSize, sudo, …). Used for risk scoring. |
| ttlMs | integer | N | — | Override 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"
}
}