Approvals
Flow supports optional human review before delivery. When a gate is configured with approvalMode: "manual", validated runs are held for approver decision before proceeding.
Approval Modes
| Mode | Behavior |
|---|---|
auto (default) | Runs proceed directly after validation — no human review |
manual | Runs wait at pending_approval status until an approver approves or rejects |
Configuring Approvers
Set up approval when creating or updating a gate:
{
"approvalMode": "manual",
"approvalConfig": {
"approvers": [
{ "type": "internal", "email": "[email protected]" },
{ "type": "external", "email": "[email protected]" }
],
"timeout_hours": 48
}
}
Approver Types
| Type | Description |
|---|---|
internal | A user with a Rynko account on your team — reviews via the dashboard |
external | Any email address — reviews via a magic link sent by email |
Decision Logic
Flow uses any-approves logic:
- The first approval moves the run to
approvedand triggers delivery immediately - The first rejection moves the run to
rejected(terminal) — no delivery
Once a decision is made, other approvers cannot override it.
Internal Approvers
Internal approvers review runs directly in the Rynko dashboard.
Dashboard Inbox
curl https://api.rynko.dev/api/flow/approvals \
-H "Authorization: Bearer YOUR_API_KEY"
Returns all pending approvals assigned to the authenticated user.
Making a Decision
# Approve
curl -X POST https://api.rynko.dev/api/flow/approvals/APPROVAL_ID/approve \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "comment": "Looks good, approved." }'
# Reject
curl -X POST https://api.rynko.dev/api/flow/approvals/APPROVAL_ID/reject \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "comment": "Amount exceeds policy limit." }'
The comment field is optional (max 1000 characters).
External Approvers (Magic Links)
External approvers receive an email with a magic link that takes them to a review portal — no Rynko account required.
How It Works
- A run reaches
pending_approvalstatus - Flow sends an email to each external approver with a unique magic link
- The approver clicks the link and enters their email as identity verification
- They receive a short-lived review token (valid for 2 hours)
- They can view the run details and approve or reject
Review Portal Endpoints
These endpoints are public (no API key required) — they use the magic link token for authentication.
Authenticate
curl -X POST https://api.rynko.dev/api/flow/review/authenticate \
-H "Content-Type: application/json" \
-d '{
"token": "MAGIC_LINK_TOKEN",
"email": "[email protected]"
}'
Response:
{
"reviewToken": "eyJ...",
"expiresIn": 7200
}
The email must match the token's intended recipient (identity challenge).
View Inbox
curl https://api.rynko.dev/api/flow/review/inbox \
-H "Authorization: Bearer REVIEW_TOKEN"
View Approval Details
curl https://api.rynko.dev/api/flow/review/approvals/APPROVAL_ID \
-H "Authorization: Bearer REVIEW_TOKEN"
Returns the approval record with full run context (payload, validation results, gate info).
Approve or Reject
# Approve
curl -X POST https://api.rynko.dev/api/flow/review/approvals/APPROVAL_ID/approve \
-H "Authorization: Bearer REVIEW_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "comment": "Approved after review." }'
# Reject
curl -X POST https://api.rynko.dev/api/flow/review/approvals/APPROVAL_ID/reject \
-H "Authorization: Bearer REVIEW_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "comment": "Data does not match requirements." }'
Resend Magic Link
If the approver didn't receive the email or the link expired:
curl -X POST https://api.rynko.dev/api/flow/review/resend \
-H "Content-Type: application/json" \
-d '{
"token": "ORIGINAL_MAGIC_LINK_TOKEN",
"email": "[email protected]"
}'
AI Judge → Approval Routing
AI Judge results can automatically trigger human review in three ways:
Low Confidence
When any criterion's confidence score falls below aiJudgeConfidenceThreshold (default 0.7), the run is automatically routed to human approval — regardless of the gate's normal approval configuration.
On Fail → Review
Set aiJudgeOnFail: "review" on your gate to route AI Judge failures to human approval instead of hard-rejecting. This lets approvers override the AI's judgment:
{
"aiJudgeEnabled": true,
"aiJudgeOnFail": "review",
"approvalMode": "manual",
"approvalConfig": {
"approvers": [
{ "type": "internal", "email": "[email protected]" }
]
}
}
AI Judge Verdict in Approval Expressions
When AI Judge completes, its verdict is injected into the approval condition expression context as aiJudge. This enables sophisticated routing:
// Route to approval whenever AI Judge fails
aiJudge.overall === 'fail'
// Route when any criterion has moderate confidence
aiJudge.criteria.some(c => c.confidence < 0.8)
// Combine with business logic
amount > 10000 || aiJudge.criteria.some(c => c.verdict === 'fail')
Available fields in aiJudge:
| Field | Type | Description |
|---|---|---|
aiJudge.overall | 'pass' or 'fail' | Overall AI Judge verdict |
aiJudge.summary | string | e.g. "2 of 3 criteria passed" |
aiJudge.criteria | array | Per-criterion results |
aiJudge.criteria[].criterion | string | The criterion text |
aiJudge.criteria[].verdict | 'pass' or 'fail' | Individual verdict |
aiJudge.criteria[].confidence | number | 0.0-1.0 confidence score |
Timeout
If no approver makes a decision within the configured timeout_hours, the approval expires. Configure the timeout (1-720 hours) in the gate's approvalConfig.
Delivery Configuration for Human-in-the-Loop Workflows
When using manual approval, consider how your application will receive the reviewer's decision.
Production Best Practice: Webhooks
For production human-in-the-loop workflows, configure a webhook delivery channel on the gate. This way, once an approver approves or rejects a run, Flow automatically delivers the result to your endpoint — no polling required.
{
"approvalMode": "manual",
"deliveryChannels": [
{
"type": "webhook",
"url": "https://your-app.com/webhooks/flow",
"secret": "whsec_..."
}
]
}
This eliminates the need to poll the API and reduces latency between the reviewer's decision and your application's reaction.
Without Delivery Channels
If no delivery channels are configured, the run will still move to approved or rejected status after the reviewer decides, but no outbound notification is sent. In this case, you must:
- Poll the API: Call
GET /api/flow/runs/:idperiodically to check the run status - Use MCP tools: Use the
get_run_statusMCP tool from an AI agent to check the outcome
This approach works for prototyping and low-volume use cases but is not recommended for production — it adds latency and consumes unnecessary API calls.
Approval Limits by Plan
| Plan | Max Approvers per Gate |
|---|---|
| Free | 5 |
| Starter | 10 |
| Growth | 25 |
| Scale | Unlimited |