Approval
An Approval is a Task to allow or refuse one pending Action that exceeds a Limit or a Mandate.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
verdict | string | One of allowed or refused. | |
deadline | datetime | When an unanswered Approval cancels the Action. |
Inherited from Task: 3 properties
| Property | Type | Required | Description |
|---|---|---|---|
due | datetime | When the Task is due. | |
priority | integer | Its rank in the inbox, highest first. | |
recurrence | string | The schedule a recurring duty repeats on. |
Inherited from Action: 11 properties
| Property | Type | Required | Description |
|---|---|---|---|
status | string | required | One of pending, done, failed, canceled, or refused. |
reason | string | Why a pending Action waits: queued, running, retrying, awaitingWorker, awaitingInput, awaitingSource, awaitingAction, or awaitingTime. | |
mode | string | required | How the caller invoked it: try, send, or do. |
by | Worker | The Worker that performed it. | |
for | Party | The Party it was done on behalf of. | |
via | string | The Interface or request it arrived through. | |
on | string | The Action or Event that caused it, such as the failed attempt a retry follows. | |
thing | string | The record, at the Version the Action was decided against. | |
input | json | The input it was called with. | |
createdAt | datetime | When it was called. | |
reserved | money | The most this Action may cost, held against its Limits from the check until it settles, billed misses included. |
Object
{
"$id": "https: //api.sb/ shiftnote.app/ approvals/ approval_1",
"$type": "Approval",
"status": "pending",
"reason": "queued",
"mode": "try",
"by": "worker_1",
"for": "party_1",
"via": "API",
"on": "Invoice.overdue",
"thing": "invoice_1@v3",
"input": {},
"createdAt": "2026-10-22T12: 00: 00Z",
"reserved": {
"amount": 79,
"currency": "USD"
},
"due": "2026-10-22T12: 00: 00Z",
"priority": 1,
"recurrence": "Weekly on Friday",
"verdict": "allowed",
"deadline": "2026-10-22T12: 00: 00Z"
}Relationships
- An Approval gates one Action: an Approval holds one pending Action that exceeds a Limit or Mandate until someone allows or refuses it.
gates, inverseapprovals.
Lifecycle
| State | Meaning | Moves to |
|---|---|---|
pending (first) | An Action waits on the answer. | approved by Approval.approve; refused by Approval.refuse; expired on Approval.expired when nobody answered by its deadline |
approved | The Action it held runs. | None: it ends here. |
refused | The Action it held ends refused, with the reason given. | None: it ends here. |
expired | Nobody answered by the deadline, so the Action it held ends canceled. | None: it ends here. |
Verbs
createCreates an Approval.POST /{namespace}/approvalsgetReturns one Approval by id.GET /{namespace}/approvals/{id}listReturns Approvals, newest first, one page at a time.GET /{namespace}/approvalsupdateChanges the given properties of an Approval.PATCH /{namespace}/approvals/{id}deleteDeletes an Approval; its history is kept.DELETE /{namespace}/approvals/{id}approveApprove allows the pending Action an Approval holds, and that Action runs.POST /{namespace}/approvals/{approval}/approverefuseRefuse declines an Approval, which sets the Action it holds to `refused` with the reason given.POST /{namespace}/approvals/{approval}/refuse
Events
Events no Verb emits. Each Verb’s Event is in the Verbs table above; every Event is delivered in the same shape.
Approval.expired
Nobody answered an Approval by its deadline, so the Action it held ends. Raised by a clock.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | required | Approval.expired. |
at | datetime | required | When it happened. |
seq | integer | Its position in the namespace, one more than the Event before; any seq is a cursor into the history. | |
data | json | required | The Thing as the Action left it. |
data.approval | Approval | required | The Approval as the change left it. |
data.approval.status | string | required | One of pending, done, failed, canceled, or refused. |
data.approval.reason | string | Why a pending Action waits: queued, running, retrying, awaitingWorker, awaitingInput, awaitingSource, awaitingAction, or awaitingTime. | |
data.approval.mode | string | required | How the caller invoked it: try, send, or do. |
data.approval.by | Worker | The Worker that performed it. | |
data.approval.for | Party | The Party it was done on behalf of. | |
data.approval.via | string | The Interface or request it arrived through. | |
data.approval.on | string | The Action or Event that caused it, such as the failed attempt a retry follows. | |
data.approval.thing | string | The record, at the Version the Action was decided against. | |
data.approval.input | json | The input it was called with. | |
data.approval.createdAt | datetime | When it was called. | |
data.approval.reserved | money | The most this Action may cost, held against its Limits from the check until it settles, billed misses included. | |
data.approval.due | datetime | When the Task is due. | |
data.approval.priority | integer | Its rank in the inbox, highest first. | |
data.approval.recurrence | string | The schedule a recurring duty repeats on. | |
data.approval.verdict | string | One of allowed or refused. | |
data.approval.deadline | datetime | When an unanswered Approval cancels the Action. |
{
"$id": "https: //api.sb/ shiftnote.app/ events/ event_1",
"$type": "Event",
"name": "Approval.expired",
"at": "2026-10-22T12: 00: 00Z",
"data": {
"approval": {
"$id": "https: //api.sb/ shiftnote.app/ approvals/ approval_1",
"$type": "Approval",
"status": "pending",
"reason": "queued",
"mode": "try",
"by": "worker_1",
"for": "party_1",
"via": "API",
"on": "Invoice.overdue",
"thing": "invoice_1@v3",
"input": {},
"createdAt": "2026-10-22T12: 00: 00Z",
"reserved": {
"amount": 79,
"currency": "USD"
},
"due": "2026-10-22T12: 00: 00Z",
"priority": 1,
"recurrence": "Weekly on Friday",
"verdict": "allowed",
"deadline": "2026-10-22T12: 00: 00Z"
}
},
"seq": 1
}Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.
| Operation | owner | admin | member | viewer |
|---|---|---|---|---|
Approval.create | Yes | Yes | Yes | No |
Approval.get | Yes | Yes | Yes | Yes |
Approval.list | Yes | Yes | Yes | Yes |
Approval.update | Yes | Yes | Yes | No |
Approval.delete | Yes | Yes | Yes | No |
Approval.approve | Yes | Yes | Yes | No |
Approval.refuse | Yes | Yes | Yes | No |
Call it
Approve the latest approval.Example
The Sales Lead offers a 30% discount when its Limit is 20%. An Approval lands in the Founder's inbox, and the Quote goes out once approved.