Use case

Human-in-the-loop review for AI agent output

Some agent outputs need a human to approve before action — outbound emails, refunds, ticket triage. Instadash gives you an editable shared grid: the agent writes rows, humans tick approval columns, the agent reads back exactly which rows changed and acts.

The pattern

Three nouns, two arrows: agent → grid → human → grid → agent.

  1. The agent writes a batch of candidate actions to a grid — refunds to approve, emails to send, tickets to escalate. Each row is one decision.
  2. A human opens the grid (https://instadash.io/<handle>/<slug>) and reviews. They tick an approved column or edit a reply_text cell. Edits are persisted server-side, immediately.
  3. The agent polls /edits?source=human&since=<timestamp> and gets back a flat list of changed cells. It filters to the approved rows and acts on them.

No queue, no Slack bot, no email digest. The grid IS the review surface and the integration surface.

Setup

Push the candidate rows with an actions schema that includes an approved checkbox column:

await fetch('https://instadash.io/ingest', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.INSTADASH_API_KEY}`,
    'Content-Type': 'application/json',
    'X-Grid-Slug': 'refunds-pending-review',
    'X-Grid-Editable': 'true',
  },
  body: JSON.stringify(
    refundCandidates.map(r => ({
      customer_id: r.customer_id,
      amount: r.amount,
      reason: r.reason,
      approved: false, // human flips this
    })),
  ),
})

Send the URL to whoever reviews:

https://instadash.io/<handle>/refunds-pending-review

Read back the human's decisions

After review, the agent reads /edits:

const r = await fetch(
  `https://instadash.io/<handle>/refunds-pending-review/edits?source=human&since=${lastChecked}`,
  { headers: { Authorization: `Bearer ${process.env.INSTADASH_API_KEY}` } },
)
const { edits } = await r.json()
// edits: [{ row, col, value, source: 'human', timestamp }, …]
 
const approvedRowIds = edits
  .filter(e => e.col === 'approved' && e.value === true)
  .map(e => e.row)
 
for (const rowId of approvedRowIds) {
  await issueRefund(rowId)
}

The agent keeps a lastChecked timestamp in its own state to avoid double-processing.

Framework integrations

  • LangGraph — drop the read-edits step into a graph node, route to "act" only when approvals are present. See /recipes/langgraph-instadash.
  • CrewAI — model the reviewer as an external resource the crew waits on. See /recipes/crewai-instadash.
  • Custom — any agent that can make HTTP requests can use this pattern.

When this beats other approaches

A Slack bot for approvals works for one-off cases. A spreadsheet works until you need an audit trail. An admin dashboard works until you want to wire a different agent in.

The grid is the shared substrate: human-friendly UI, agent-friendly API, immutable history. The reviewer doesn't need a developer to ship an approval flow, and the next agent that needs the same human signal reuses the same grid.

FAQ

How does the agent know which rows the human approved?

Every grid exposes an /edits endpoint that returns the diff since a given timestamp. Filter by source=human to get only operator-touched cells. The agent polls this endpoint (or queries on demand) and acts on the approved set.

Can a human approve in bulk or only one row at a time?

Both. The grid UI supports keyboard navigation, range selection, and column-level fill, so an operator can approve hundreds of rows in seconds. Each cell change is still recorded individually in /edits.

Does the agent see partial approvals?

Yes — /edits is a live diff stream. If a human approves five rows and pauses, the agent can already act on those five before the rest are decided.

What stops the agent from acting on rows the human hasn't approved yet?

The action is your code, not Instadash. The pattern is to gate on a specific cell value (e.g. approved=true) before acting. /edits returns the cell values along with the change so the gate is trivial.

Related

More use cases

Related across sections

canonical: /use-cases/human-in-the-loop-agent-review