Skip to main content
POST
Decide a permission ask

Authorizations

Authorization
string
header
required

Enforced on every transport, with no exempt operation. A person's request — over the default local unix socket exactly as over a TCP listener — must carry a JWT issued by the configured identity provider, which the host verifies itself against that issuer's key set: signature, issuer, expiry, and the person and organisation it names. Requests without a valid one receive 401 NRY-0011. The socket's file permissions are transport and are not an authorisation.

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
decidedBySubject
string

WHICH person decided, beside decidedBy's kind. Any member of the organisation may decide a permission request raised on a conversation that is not marked private, so a decision taken for somebody else is an ordinary event here and this is the only field that distinguishes it from one the owner took.

Absent when no person decided — a rule or a hook answered, or the ask expired with nobody answering — and absent on every decision this host recorded before it began recording who. An absent value is never the owner: a decision nobody recorded and a decision the owner took are different facts, and reading one as the other would attribute commands to people who never approved them.

Maximum string length: 256
ownerSubject
string

Whose conversation the decision was taken ON — the owner this request was parked on, which is the same value the session's own owner field carries. Read beside decidedBySubject it answers "who let that command run, and on whose work", from the audit alone.

Absent only for a row this host can attribute to nobody: a refinement round whose conversation was purged before owners were recorded.

Maximum string length: 256
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.