← Back to Cookbook

Document Approval Workflow

Multi-stage document approval with human review gates and revision requests.

aiswitchresend

Source

/**
 * Multi-stage document approval workflow with human review gates.
 */

form document_submission {
  label: "Submit Document"
  schema: @json {
    {
      "type": "object",
      "required": ["title", "content", "author", "doc_type"],
      "properties": {
        "title": { "type": "string", "title": "Document Title" },
        "content": { "type": "string", "title": "Document Content" },
        "author": { "type": "string", "title": "Author" },
        "doc_type": {
          "type": "string",
          "title": "Document Type",
          "enum": ["policy", "procedure", "contract", "proposal"]
        }
      }
    }
  }
}

graph approve_document {
  label: "Document Approval"

  root {
    type: code
    label: "Extract document"
    code: @ts { return context.nodes.root.input }
    outputSchema: @json {
      {
        "type": "object",
        "properties": {
          "title": { "type": "string" },
          "content": { "type": "string" },
          "author": { "type": "string" },
          "doc_type": {
            "type": "string",
            "enum": ["policy", "procedure", "contract", "proposal"]
          }
        }
      }
    }
  }

  node quality_check {
    type: ai
    label: "Quality check"
    kind: object
    model: "google/gemini-2.5-flash"
    prompt: @ts {
      const doc = context.nodes.root.output
      return "Review this " + doc.doc_type + " document for quality. Check grammar, clarity, completeness, and formatting.\n\nTitle: " + doc.title + "\n\n" + doc.content
    }
    schema: @json {
      {
        "type": "object",
        "required": ["quality_score", "issues"],
        "properties": {
          "quality_score": { "type": "number", "minimum": 0, "maximum": 100 },
          "issues": { "type": "array", "items": { "type": "string" } },
          "suggestions": { "type": "array", "items": { "type": "string" } }
        }
      }
    }
  }

  node manager_review {
    type: code
    label: "Manager review"
    code: @ts {
      const doc = context.nodes.root.output
      const qc = context.nodes.quality_check.output
      return {
        title: doc.title,
        author: doc.author,
        type: doc.doc_type,
        quality_score: qc.quality_score,
        issues: qc.issues,
        suggestions: qc.suggestions,
        content_preview: doc.content.substring(0, 500) + "..."
      }
    }
    review: {
      enabled: true
      title: "Manager Approval Required"
      description: "Review this document and approve or request revisions."
      schema: @json {
        {
          "type": "object",
          "required": ["decision"],
          "properties": {
            "decision": { "type": "string", "enum": ["approve", "revise", "reject"], "title": "Decision" },
            "comments": { "type": "string", "title": "Comments" }
          }
        }
      }
      actions: [
        { id: "approve", label: "Approve", outcome: "approve" },
        { id: "revise", label: "Request Revisions", outcome: "reject" },
        { id: "reject", label: "Reject", outcome: "reject" }
      ]
    }
  }

  node route_decision {
    type: switch
    label: "Route decision"
    cases: ["approved", "needs_work"]
    router: @ts {
      const review = context.reviews.manager_review
      if (review.decision === "approve") return "approved"
      return "needs_work"
    }
  }

  node publish {
    type: code
    label: "Mark as published"
    code: @ts {
      return {
        status: "published",
        title: context.nodes.root.output.title,
        author: context.nodes.root.output.author,
        published_at: new Date().toISOString()
      }
    }
  }

  node request_revisions {
    type: resend
    label: "Request revisions"
    from: @ts { return "[email protected]" }
    to: @ts { return "[email protected]" }
    subject: @ts { return "Revisions requested: " + context.nodes.root.output.title }
    text: @ts {
      const review = context.reviews.manager_review
      return "Your document requires revisions.\n\nDecision: " + review.decision + "\nComments: " + (review.comments || "No additional comments.")
    }
  }

  flow {
    root -> quality_check
    quality_check -> manager_review
    manager_review -> route_decision
    route_decision -["approved"]-> publish
    route_decision -["needs_work"]-> request_revisions
  }
}

trigger on_doc_submit {
  form:document_submission -> approve_document
  enabled: true
}

Flow

Trigger → graph

Graph nodes