OCNORA
hitlbeginner10 min setupby Mantle

AI screen, human approve, then send

Classify an inbound request, ask a reviewer to approve or reject it, and email the requester the outcome.

Drag to pan, pinch to zoom. This is the real graph the template installs.

How it works

What it does

  • Webhook approval-requestRisk screen (low_risk | needs_review | high_risk) gives the reviewer context.
  • Reviewer decision pauses the run until someone picks Approve / Reject (48 h timeout).
  • Approved? routes to the approval or rejection email.

Setup

  • AI connection on Risk screen; SendGrid/SMTP on both send nodes.
  • Optional: add a second branch before the reviewer to auto-approve low_risk requests.

Nodes 6

  1. 01Request intrigger.webhookTrigger
  2. 02Risk screenai.classifyAI
  3. 03Reviewer decisionhuman.promptHuman
  4. 04Approved?control.branchLogic
  5. 05Send approvalmail.sendDestination
  6. 06Send rejectionmail.sendDestination
View template JSON
{
  "format": "mantle-workflow/v1",
  "id": "hitl-approval-gate",
  "name": "AI screen, human approve, then send",
  "graph": {
    "nodes": [
      {
        "id": "webhook",
        "type": "trigger.webhook",
        "label": "Request in",
        "config": {
          "slug": "approval-request",
          "input_schema": {
            "required": [
              "request",
              "requester_email"
            ],
            "properties": {
              "request": {
                "type": "string"
              },
              "requester_email": {
                "type": "string"
              }
            }
          }
        },
        "connection_id": null,
        "worker_type": "generic-worker",
        "position": {
          "x": 60,
          "y": 220
        }
      },
      {
        "id": "ai_classify",
        "type": "ai.classify",
        "label": "Risk screen",
        "config": {
          "model": "claude-haiku-4-5",
          "labels": [
            "low_risk",
            "needs_review",
            "high_risk"
          ],
          "text": "{{ $json.request }}",
          "temperature": 0
        },
        "connection_id": null,
        "worker_type": "ai-worker",
        "position": {
          "x": 380,
          "y": 220
        }
      },
      {
        "id": "reviewer",
        "type": "human.prompt",
        "label": "Reviewer decision",
        "config": {
          "prompt_text": "Request from {{ steps.webhook.requester_email }} (AI: {{ steps.ai_classify.response.label }}, {{ steps.ai_classify.response.confidence }}):\n\n{{ steps.webhook.request }}",
          "options": [
            {
              "id": "approve",
              "label": "Approve"
            },
            {
              "id": "reject",
              "label": "Reject"
            }
          ],
          "timeout_seconds": 172800
        },
        "connection_id": null,
        "worker_type": "generic-worker",
        "position": {
          "x": 700,
          "y": 220
        }
      },
      {
        "id": "branch_approved",
        "type": "control.branch",
        "label": "Approved?",
        "config": {
          "mode": "first_match"
        },
        "connection_id": null,
        "worker_type": "generic-worker",
        "position": {
          "x": 1020,
          "y": 220
        }
      },
      {
        "id": "send_approved",
        "type": "mail.send",
        "label": "Send approval",
        "config": {
          "to": "{{ steps.webhook.requester_email }}",
          "subject": "Your request was approved",
          "body_text": "Good news — your request has been approved.\n\n{{ steps.webhook.request }}"
        },
        "connection_id": null,
        "worker_type": "generic-worker",
        "position": {
          "x": 1340,
          "y": 30
        }
      },
      {
        "id": "send_rejected",
        "type": "mail.send",
        "label": "Send rejection",
        "config": {
          "to": "{{ steps.webhook.requester_email }}",
          "subject": "Your request was not approved",
          "body_text": "Unfortunately your request was not approved. Reply to this email if you have questions.\n\n{{ steps.webhook.request }}"
        },
        "connection_id": null,
        "worker_type": "generic-worker",
        "position": {
          "x": 1340,
          "y": 410
        }
      }
    ],
    "edges": [
      {
        "from": "webhook",
        "to": "ai_classify"
      },
      {
        "from": "ai_classify",
        "to": "reviewer"
      },
      {
        "from": "reviewer",
        "to": "branch_approved"
      },
      {
        "from": "branch_approved",
        "to": "send_approved",
        "branch": "true",
        "condition": {
          "match": "all",
          "rules": [
            {
              "left": "response",
              "op": "equals",
              "right": "approve"
            }
          ]
        },
        "label": "approve"
      },
      {
        "from": "branch_approved",
        "to": "send_rejected",
        "branch": "false",
        "label": "reject",
        "priority": 1
      }
    ]
  }
}

Use it, then make it yours.

Templates install as drafts. Change anything, and Workflow Health re-checks the graph on every save.