Documentation
DocsConcepts

Approval pipelines

How approval pipelines decide what happens next for a request.

Updated Oct 5, 2026

An approval pipeline is a series of blocks that runs on a request to decide what happens next. Blocks can check request fields, ask a model or service, return a fixed outcome, run a set of blocks only for the requests that need them, or hand a request to another pipeline.

Blocks

Blocks run one at a time, in order. Each block can do one of four things.

OutcomeWhat happens
ApproveThe request is approved and the pipeline stops.
DenyThe request is denied and the pipeline stops.
Ask for human reviewThe pipeline stops and the request goes to human review.
ContinueThe request moves to the next block.

An Outcome block returns the outcome you choose for every request that reaches it. It is useful as the last block to handle everything earlier blocks passed on. Nothing below it runs. See Outcome blocks to set one up.

A Branch block holds its own blocks and runs them only for requests that match its condition. Every other request skips the branch and moves on to the next block. A request that enters a branch is settled there: if none of the branch's blocks decides, it goes to human review, and nothing after the branch runs. See Branch blocks to set one up.

A Delegate block hands the requests that match its condition to another pipeline. Every other request skips it. See Delegate blocks to set one up.

Pipeline evaluation

Every request starts in the entry pipeline. Your organization has exactly one, and it runs for every request from every agent.

You can create more pipelines, for example one for each team that owns a set of rules. A request only reaches another pipeline when a Delegate block hands it over. Requests that don't match any Delegate block stay in the pipeline they're in.

A request that is handed to another pipeline is settled there. If that pipeline approves, denies or asks for human review, processing stops. If none of its blocks decides, the request goes to human review, and nothing after the Delegate block runs.

If no pipeline decides, the request needs human review. Whenever a request needs a human, an escalation path handles who is contacted.

Loading diagram…

Diagram source
mermaid
flowchart TD
    request["Request from an agent"] --> entryBlocks

    subgraph entry["Entry pipeline"]
        entryBlocks["Check the blocks in order"]
    end

    subgraph delegated["Delegated pipeline"]
        delegatedBlocks["Check the blocks in order"]
    end

    entryBlocks -->|"Approve or deny"| decision["Decision returned to the agent"]
    entryBlocks -->|"Delegate block matches"| delegatedBlocks
    entryBlocks -->|"Ask a human or no decision"| review["Human review"]
    delegatedBlocks -->|"Approve or deny"| decision
    delegatedBlocks -->|"Ask a human or no decision"| review

Organizing your pipelines

How you set up your pipelines depends on who writes the rules. Rules that apply to every request go in the entry pipeline. If a team wants to manage its own rules, create a pipeline for that team. Then add a Delegate block to the entry pipeline that sends the team the requests it should handle.

Put shared rules above the Delegate blocks, so they check every request first. Delegate blocks run in order, and a request goes to the first one it matches.

To let a team change only its own pipeline, give them pipeline permissions for it.

Pipelines are flexible, so you can shape them around how your organization works. Here are some common setups to start from. You can combine them or build your own.

Split by agent

Use this when each team runs its own AI agents.

  • Your security team owns the entry pipeline. It holds the rules every agent must follow.
  • Each team owns a pipeline for its agents.
  • A Delegate block in the entry pipeline checks Agent slug and hands each agent's requests to its team's pipeline.

Loading diagram…

Diagram source
mermaid
flowchart TD
    request["Request from an agent"] --> shared

    subgraph entry["Entry pipeline, owned by security"]
        shared["Rules for every agent"]
        delegates["Delegate blocks by agent"]
        shared -->|Continue| delegates
    end

    delegates -->|"Agent slug equals support-assistant"| support["Support pipeline, owned by the support team"]
    delegates -->|"Agent slug equals deploy-bot"| platform["Deploys pipeline, owned by the platform team"]

Split by MCP server

Use this when each team owns an MCP server, such as a database server or a payments server.

  • The entry pipeline holds the rules for every server.
  • Each team owns a pipeline for its server.
  • A Delegate block in the entry pipeline checks MCP server and hands that server's requests to its team's pipeline.

Requests to the database server go to the database team's pipeline, whichever agent sends them.

Loading diagram…

Diagram source
mermaid
flowchart TD
    request["Request from an agent"] --> shared

    subgraph entry["Entry pipeline"]
        shared["Rules for every server"]
        delegates["Delegate blocks by server"]
        shared -->|Continue| delegates
    end

    delegates -->|"MCP server equals postgres"| database["Database pipeline, owned by the data team"]
    delegates -->|"MCP server equals stripe"| payments["Payments pipeline, owned by the payments team"]

Keep it in one pipeline

If one team owns all the rules, you don't need more pipelines. Use Branch blocks in the entry pipeline to group rules by agent, server or tool. Add a pipeline when another team should own and change its rules on its own, or when the entry pipeline gets too big to find your way around. Moving a group of rules into its own pipeline keeps each one short and easier to manage.

An example: issuing a refund

This example follows the split by agent setup.

Imagine an agent called Support assistant handles your refunds. Your security team owns the entry pipeline, and your support team owns a pipeline called Support.

Entry pipeline, owned by the security team:

  1. Deny refunds over $500.
  2. Hand requests from Support assistant to the Support pipeline.

Support pipeline, owned by the support team:

  1. Approve refunds up to $20.
  2. Ask for human review for refunds over $20, up to $500.

Loading diagram…

Diagram source
mermaid
flowchart TD
    request["Refund request from Support assistant"] --> limit

    subgraph entry["Entry pipeline, owned by security"]
        limit["Deny refunds over $500"]
        delegate["Hand Support assistant requests to Support"]
        limit -->|Continue| delegate
    end

    subgraph support["Support pipeline, owned by the support team"]
        small["Approve refunds up to $20"]
        larger["Ask for human review over $20, up to $500"]
        small -->|Continue| larger
    end

    limit -->|Matches| denied["Denied"]
    delegate -->|Matches| small
    small -->|Matches| approved["Approved"]
    larger --> review["Human review"]

Here's what happens to three refunds.

RefundDecided byResult
$10Support pipeline: approve refunds up to $20Approved
$75Support pipeline: ask for human review over $20An escalation path brings in a reviewer
$600Entry pipeline: deny refunds over $500Denied, before the Support pipeline runs

The support team can change its own rules, for example raising its $20 limit, without touching the entry pipeline. The $500 limit still applies to every refund, because the security team owns it.