Skip to main content
POST
Decide a permission ask

Path Parameters

permissionId
string<uuid>
required

The permission ask's id.

Body

application/json

A decision on a pending permission ask. A denial is never remembered: remember must be none (or absent) whenever decision is deny, and any other combination is refused with 400. The host offers four answers — allow once, allow always in this project, allow always globally, and deny with a message — so a remembered refusal is not one of them, and the prompt states before the answer exists that remembering would grant an allow rule.

decision
enum<string>
required
Available options:
allow,
deny
remember
enum<string>
default:none

Scope to remember the decision at — none decides only this ask, session remembers for this session, project appends a rule scoped to this repository, global appends a rule to the user's own configuration. Two "always" scopes rather than one because "fine in THIS repository" and "fine everywhere I work" are different claims, and a single "always" forces everybody to over-grant. Only an allow is remembered; any scope other than none alongside decision=deny is a 400.

Available options:
none,
session,
project,
global
message
string

Optional correction shown to the agent, useful on deny. It reaches the model verbatim as something it can act on: a denial saying only "denied" teaches nothing, so the model tries a variant and burns the turn, while "do not push to main, open a PR branch instead" ends the exchange.

Maximum string length: 4096

Response

The recorded decision. This operation answers a PENDING ask and 409s anything else, so decidedBy and decidedAt are always present in this response. They are optional on the shared schema because the audit listing reports expired entries too, which nobody decided — a client reading only this operation may treat both as guaranteed.

One entry of the decision audit. There is exactly one decision per ask, so the decision's id IS the ask's id: id and permissionId carry the same identifier.

WHAT IS RECORDED, stated here because it is narrower than "every decision" and deliberately so: every ASK and its answer, whoever answered it, and every DENY — including the ones taken without asking anybody, which is the shape almost every denial has in unattended work. A rule-derived ALLOW on a free action is NOT a row. Recording every permitted read would write thousands of rows per session and bury the two questions this audit exists to answer — what was denied and why, and what did I approve.

AND A DECISION IS NOT A RECORD OF EXECUTION. It says what was decided, not what ran: a call interrupted in the same instant its decision was released can leave an allow here for something that never happened. Read it as the trail of judgements, and the transcript for what followed.

id
string<uuid>
required
permissionId
string<uuid>
required
decision
enum<string>
required

expired is an ask nobody answered before the call it was blocking ended. It carries no decider and no decidedAt, because "nobody answered" is itself the finding when somebody asks why an action did not happen.

Available options:
allow,
deny,
expired
requestedAt
string<date-time>
required

When the decision was raised. It is what the audit is ordered and paged by, so a page boundary is stable while new decisions land.

sessionId
string<uuid>
action
string
Maximum string length: 128
resource
string
Maximum string length: 4096
decidedBy
enum<string>

Who decided — a human, the policy deciding without asking (which includes a rule that landed after the ask was raised and covered it), or the configured executable at the permission-ask decision point. Absent on an expired entry.

Available options:
user,
policy,
hook
reason
string

Why, in the decider's own words: a person's message on a deny-with-message, the rule a policy refusal names, or the hook that decided — a hook denial that does not say which hook is an audit trail ending in a shrug. Absent when the outcome speaks for itself.

Maximum string length: 4096
remember
enum<string>
Available options:
none,
session,
project,
global
decidedAt
string<date-time>

Absent on an expired entry, which nobody decided.