Read and decide the requests for approval
List the requests for approval that executions wait on, and approve or reject one.
An approval is a request for a person's decision that an execution waits on. A Require Approval node raises one, and so does an AI Model node whose overrun policy asks a person. The operations of this page list the requests and decide one. The console shows the same requests on Approvals.
Who reads and who decides
Reading the requests takes management:read. Deciding one takes management:write, because a decision lets an execution spend money. The audit log records every decision.
A key reads and decides the requests of its own project. A request of another project, or one that does not exist, answers 404 with the code approval_not_found.
The list is the newest first and is read page by page, as Requests describes. Send status=pending to read only the requests that still wait.
What a decision does
The body is optional. Its
reason, of up to 500 characters, is kept with the approval and shows in the trace of the execution.The answer is the approval with its new
statusanddecided_at. A decision made with an API key names the key indecided_by. A decision made in the console names the person indecided_by_user_idanddecided_by_name.The request does not move the execution. The execution goes on within a minute: through the Approved port after an approval, and through Rejected after a rejection.
A request from a model node is decided the same way. An approval lets the model call go on with a new hold, and a rejection refuses it with
overrun_stopped— Approvals says what follows.Slen sends the webhook event
approval.decidedwith the verdict.
A request that can no longer be decided
A request waits until expires_at. The author of a Require Approval node sets the time on the node, and a request from a model node waits 24 hours. When nobody decides in time, the request becomes expired within a minute, and approval.decided says expired. From then on it goes as after a rejection: the execution leaves a Require Approval node through Rejected, and the call of a model node is refused with overrun_stopped.
A request that is no longer
pendinganswers409with the codeapproval_already_decided: somebody decided it, or it has expired.A request that is still
pendingwhen its time has passed answers409with the codeapproval_expired. It is closed asexpiredat once, as if nobody had decided, and the execution goes on as after a rejection.A request whose execution has ended and no longer waits answers the same
approval_expired. It is closed asexpired, nothing goes on, and no webhook event is sent.
Operations
Decisions parked executions are waiting for
GET /v1/approvals
- Scope:
management:read
Parameters
| Parameter | Description |
|---|---|
limit optionalquery | Page size (default 30).
|
cursor optionalquery |
|
status optionalquery |
|
project optionalquery |
|
Responses
One cursor page of approvals
{
"data": [
{
"approval_id": "74438200973594624",
"project_id": "74438200973594624",
"execution_id": "74438200973594624",
"flow_id": "74438200973594624",
"node_key": "string",
"operation_code": "generate_email",
"note": "string",
"status": "pending",
"decided_by": "string",
"decided_by_user_id": "74438200973594624",
"decided_by_name": "string",
"reason": "string",
"expires_at": "2026-10-02T09:30:00Z",
"created_at": "2026-10-02T09:30:00Z",
"decided_at": "2026-10-02T09:30:00Z"
}
],
"meta": {
"limit": 1,
"next_cursor": "string"
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Headers
X-Request-Id
The body of every error is the error envelope.
Let a parked execution spend
POST /v1/approvals/{id}/approve
- Scope:
management:write
A person's verdict names them (decided_by_user_id, decided_by_name); a key's names the key in decided_by, as before.
409 approval_already_decided — the request has left pending: somebody decided it, its deadline passed, or its execution ended. 409 approval_expired — this very call found the request past its expires_at, or found that the execution it belongs to is no longer waiting (it failed, was lost or ran into its parked ceiling); the request is closed as expired and nothing is continued.
A verdict that is accepted is announced to webhook subscribers as approval.decided (see createWebhookEndpoint), and the node that asked leaves {status, reason, approval_id, decided_at} in its output: a refusal and a deadline nobody met both leave through rejected, and nodes.<key>.output.status (rejected / expired) tells them apart.
Parameters
| Parameter | Description |
|---|---|
id requiredpath | Snowflake identifier, as a string.
|
Request body optional
{
"reason": "string"
}Responses
The decided approval
{
"data": {
"approval_id": "74438200973594624",
"project_id": "74438200973594624",
"execution_id": "74438200973594624",
"flow_id": "74438200973594624",
"node_key": "string",
"operation_code": "generate_email",
"note": "string",
"status": "pending",
"decided_by": "string",
"decided_by_user_id": "74438200973594624",
"decided_by_name": "string",
"reason": "string",
"expires_at": "2026-10-02T09:30:00Z",
"created_at": "2026-10-02T09:30:00Z",
"decided_at": "2026-10-02T09:30:00Z"
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 409 | State conflict: idempotency clash, stale draft, illegal transition; the last owner of an organization ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Headers
X-Request-Id
The body of every error is the error envelope.
Refuse a parked execution
POST /v1/approvals/{id}/reject
- Scope:
management:write
A person's verdict names them (decided_by_user_id, decided_by_name); a key's names the key in decided_by, as before.
409 approval_already_decided — the request has left pending: somebody decided it, its deadline passed, or its execution ended. 409 approval_expired — this very call found the request past its expires_at, or found that the execution it belongs to is no longer waiting (it failed, was lost or ran into its parked ceiling); the request is closed as expired and nothing is continued.
A verdict that is accepted is announced to webhook subscribers as approval.decided (see createWebhookEndpoint), and the node that asked leaves {status, reason, approval_id, decided_at} in its output: a refusal and a deadline nobody met both leave through rejected, and nodes.<key>.output.status (rejected / expired) tells them apart.
Parameters
| Parameter | Description |
|---|---|
id requiredpath | Snowflake identifier, as a string.
|
Request body optional
{
"reason": "string"
}Responses
The decided approval
{
"data": {
"approval_id": "74438200973594624",
"project_id": "74438200973594624",
"execution_id": "74438200973594624",
"flow_id": "74438200973594624",
"node_key": "string",
"operation_code": "generate_email",
"note": "string",
"status": "pending",
"decided_by": "string",
"decided_by_user_id": "74438200973594624",
"decided_by_name": "string",
"reason": "string",
"expires_at": "2026-10-02T09:30:00Z",
"created_at": "2026-10-02T09:30:00Z",
"decided_at": "2026-10-02T09:30:00Z"
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 409 | State conflict: idempotency clash, stale draft, illegal transition; the last owner of an organization ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Headers
X-Request-Id
The body of every error is the error envelope.
Examples
Read the requests that wait
One execution waits for a decision on its node review. The note is the one the author of the flow left on the node.
curl "https://api.slen.ai/api/v1/approvals?status=pending" \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": [
{
"approval_id": "74443031076798464",
"project_id": "74438200973594624",
"execution_id": "74443028719599616",
"flow_id": "74438623450128384",
"node_key": "review",
"operation_code": "generate_email",
"note": "Check the tone before the email goes out.",
"status": "pending",
"decided_by": null,
"decided_by_user_id": null,
"decided_by_name": null,
"reason": null,
"expires_at": "2026-10-03T09:20:11+00:00",
"created_at": "2026-10-02T09:20:11+00:00",
"decided_at": null
}
],
"meta": {
"limit": 30,
"next_cursor": null
}
}Approve a request
The answer is the approval with the verdict and the key that gave it.
curl -X POST https://api.slen.ai/api/v1/approvals/74443031076798464/approve \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": {
"approval_id": "74443031076798464",
"project_id": "74438200973594624",
"execution_id": "74443028719599616",
"flow_id": "74438623450128384",
"node_key": "review",
"operation_code": "generate_email",
"note": "Check the tone before the email goes out.",
"status": "approved",
"decided_by": "74438344587730944",
"decided_by_user_id": null,
"decided_by_name": null,
"reason": null,
"expires_at": "2026-10-03T09:20:11+00:00",
"created_at": "2026-10-02T09:20:11+00:00",
"decided_at": "2026-10-02T09:31:40+00:00"
}
}Reject a request
The reason is kept with the approval. The execution leaves the node through Rejected.
curl https://api.slen.ai/api/v1/approvals/74443031076798464/reject \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "The customer asked us not to write again."
}'{
"data": {
"approval_id": "74443031076798464",
"project_id": "74438200973594624",
"execution_id": "74443028719599616",
"flow_id": "74438623450128384",
"node_key": "review",
"operation_code": "generate_email",
"note": "Check the tone before the email goes out.",
"status": "rejected",
"decided_by": "74438344587730944",
"decided_by_user_id": null,
"decided_by_name": null,
"reason": "The customer asked us not to write again.",
"expires_at": "2026-10-03T09:20:11+00:00",
"created_at": "2026-10-02T09:20:11+00:00",
"decided_at": "2026-10-02T09:31:40+00:00"
}
}Where to go next
Approvals — the same requests in the console.
Async runs and waiting — how an execution that waits for a decision answers.
Webhooks —
approval.requestedandapproval.decided, and how they arrive.Error codes — what each code of this page means and what to do about it.
Built from openapi b070e1f, sdk contract 5cc7055, php ae4a53b, node 84bc62d