RapidfolioRapidfolio
API Reference

Human Review API

Submit approve or reject decisions for runs paused at a Human Review node, and subscribe to review-needed webhook events.

Overview

A Human Review node pauses a procedure run and waits for a human decision before continuing. When a run reaches this node it transitions to awaiting_review status and stays there until a reviewer approves or rejects it.

Reviews can be submitted from:

  • The Rapidfolio dashboard (the Pending Reviews queue)
  • Programmatically via the review endpoint below
  • Your own internal tooling, using the API

Submit a Review Decision

POST https://app.rapid.io/api/runner-proxy/review/:runId
Content-Type: application/json

This endpoint requires a valid session token (dashboard session) or an API key with reviewer permissions.

Request Body

{
  "approved": true,
  "outputs": {
    "overrideAmount": 4500
  },
  "rejectionReason": "Amount exceeds policy",
  "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000"
}

Fields

FieldTypeRequiredDescription
approvedbooleanYestrue to approve and resume the run; false to reject and fail it
outputsobjectNoWhen approving, optionally override the Human Review node's output values. These replace the step's result before the run continues.
rejectionReasonstringRequired if approved: falseA human-readable reason for the rejection. Stored in run.error and visible in the run log.
idempotencyKeystring (UUID)YesA unique UUID for this submission. Prevents duplicate decisions if the request is retried. Generate a new UUID per review submission.

Note: The idempotencyKey must be a valid UUID v4. Reusing the same key for the same run returns the original decision without re-processing. Reusing it for a different run is an error.


What Happens After

Approved

The run transitions back to running and execution continues from the node immediately following the Human Review node.

If outputs were provided, they replace the Human Review node's result. Downstream nodes receive the overridden values rather than whatever the step originally produced. This is useful when a reviewer needs to correct an amount, adjust a classification, or supply data that the automated step could not determine.

Rejected

The run transitions to failed. The rejectionReason is stored in run.error and is visible in the run log and timeline. No further nodes are executed.


Success Response — 200

{ "success": true }

Error Responses

StatusMeaning
404 Not FoundThe run does not exist or is not in awaiting_review status
409 ConflictA decision has already been submitted for this run
422 Unprocessable EntityrejectionReason missing when approved is false, or idempotencyKey is not a valid UUID

Webhook: Review Needed

Subscribe to the human_review_requested event to receive a notification the moment a run pauses for review. This lets you build real-time review queues, alert reviewers via Slack or email, or integrate approvals into your internal tooling.

See Webhooks for setup instructions.

Payload

{
  "id": "delivery_xyz",
  "event": "human_review_requested",
  "createdAt": "2026-02-27T10:00:00.000Z",
  "data": {
    "runId": "run_clxyz123",
    "procedureId": "proc_abc",
    "environment": "sandbox",
    "reviewTitle": "Approve payment of $5,000",
    "reviewData": {
      "amount": 5000,
      "recipient": "Acme Corp"
    }
  }
}

Payload Fields

FieldDescription
data.runIdThe ID of the run paused for review
data.procedureIdThe procedure that contains the review node
data.environmentsandbox or live
data.reviewTitleHuman-readable title configured on the review node
data.reviewDataThe data surface configured for the reviewer to inspect

Example: Automated Review Workflow

A common pattern is to receive the webhook, present the review data to an internal approval system, and then POST the decision back to Rapidfolio once the reviewer acts:

// 1. Receive the webhook
app.post('/webhooks/rapid', async (req, res) => {
  const payload = req.body

  if (payload.event === 'human_review_requested') {
    const { runId, reviewTitle, reviewData } = payload.data

    // 2. Create an approval request in your internal system
    await internalApprovalQueue.create({
      runId,
      title: reviewTitle,
      data: reviewData,
    })
  }

  res.sendStatus(200)
})

// 3. When reviewer decides, submit back to Rapidfolio
async function submitReviewDecision(
  runId: string,
  approved: boolean,
  rejectionReason?: string
): Promise<void> {
  await fetch(`https://app.rapid.io/api/runner-proxy/review/${runId}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      approved,
      rejectionReason: approved ? undefined : rejectionReason,
      idempotencyKey: crypto.randomUUID(),
    }),
  })
}

On this page