# Human approval gates for AI agents

To pause an AI agent until a human approves a risky action, call `run.gate()`. The workflow blocks on a durable gate stored in Postgres; it can be approved or rejected from Tidebase Studio, your own product UI, a Slack-style adapter, or a plain webhook — and the decision survives crashes and replays exactly once.

```typescript
const decision = await run.gate('approve-send', {
  prompt: 'Send this report to the customer?',
  data: { reportId },
  channels: [{ type: 'webhook', url: process.env.REVIEW_WEBHOOK_URL! }],
  capability: {
    name: 'report.send',
    scopes: ['report:send'],
    reason: 'agent wants to send an external report'
  }
})

if (decision.decision !== 'approved') {
  throw new Error('Report was not approved')
}
```

Tidebase is an open-source checkpoint layer for AI agents: wrap your steps, and failed runs resume from the last safe point — in your own Postgres, without moving execution into a new runtime.

## Gate semantics

- **Durable:** the gate is a Postgres row, not an in-memory promise. The process can die while waiting; on resume, a still-pending gate keeps waiting, and an already-resolved gate replays its decision.
- **Exactly-once:** resolution requires the gate's `resolveToken` and only succeeds while the gate is `pending`. A second resolve attempt gets a 409, not a double-approval.
- **Decisions:** `approved`, `rejected`, or `canceled`, with optional `actor` and payload recorded for audit.

## Delivering the gate to a reviewer

Webhook channels push gate events to any surface you own:

```typescript
await tide.run('generate-report', {
  input: { topic: 'channels' },
  channels: [{
    type: 'webhook',
    url: 'https://your-app.example.com/api/tidebase-events',
    events: ['run.failed', 'step.failed', 'gate.created']
  }]
}, workflow)
```

The webhook payload for `gate.created` includes a `resolveUrl` and `resolveToken`, so the receiving surface can render an approve/reject UI and resolve directly:

```bash
curl -X POST "$RESOLVE_URL" \
  -H 'content-type: application/json' \
  -d '{"token":"<resolveToken>","decision":"approved","actor":"yao"}'
```

A slow or hung channel endpoint never blocks other writers to the run.

## Capability metadata

The `capability` field (name, scopes, reason) is **audit metadata only** — Tidebase records what the agent asked permission for, but does not store or broker API keys or credentials.

See also: [The replay contract](replay-contract-is-it-safe-to-rerun.md) · [Quickstart](quickstart.md) — `pnpm example:review` runs a local approval surface you can click through.