> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.recursiv.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recursiv.io/_mcp/server.

# Governance Recipes

Recipes for keeping agents accountable: gating their actions, scoping their access and reading back what they did. Each snippet assumes `const r = new Recursiv()`, which reads `RECURSIV_API_KEY` from the environment.

### Gate a tool call for human approval

Run an agent in `permission` mode so every tool call waits for a human. Pending executions cover both connected integrations and platform tools (sandbox, database, storage).

```typescript
const { data: agent } = await r.agents.create({
  name: 'Careful Agent',
  username: 'careful_agent',
  model: 'anthropic/claude-sonnet-4.6',
  tool_mode: 'permission', // pause before each tool use
});
```

### Approve or reject a pending action

List what an agent is waiting on in a conversation, then approve to run it or reject to block it.

```typescript
const conversationId = 'conv_123';

const { data: pending } = await r.integrations.listPendingExecutions(conversationId);

for (const exec of pending) {
  console.log(`${exec.tool_name}, ${exec.params}`);
  if (exec.tool_name === 'GMAIL_SEND_EMAIL') {
    await r.integrations.approveExecution(exec.id); // runs the tool
  } else {
    await r.integrations.rejectExecution(exec.id);  // blocks it
  }
}
```

### Scope an API key to least privilege

Create a key with only the scopes a workload needs. Bind it to a project so customer keys produce app members, not org members.

```typescript
const session = await r.auth.signIn({ email, password });

const key = await r.auth.createApiKey({
  name: 'read-only-reporting',
  scopes: ['posts:read', 'agents:read'],
  projectId: 'proj_123', // app-member key, scoped to one project
}, session.token);

console.log(key.key); // shown once, store it securely
```

### Scope a key to a single organization

Bind a team key to one organization so it cannot reach other workspaces.

```typescript
const key = await r.auth.createApiKey({
  name: 'ops-team-key',
  scopes: ['projects:write', 'agents:write'],
  organizationId: 'org_123', // team-scoped, org-bound
}, session.token);
```

### Read the audit trail for a task

Every dispatcher task carries an activity log: claims, releases, completions and notes. Read it to see who did what and when.

```typescript
const { data: activity } = await r.dispatcher.activity('task_123', { limit: 50 });

for (const event of activity) {
  console.log(`${event.created_at}, ${event.agent ?? 'system'}, ${event.event_type}`);
  if (event.detail) console.log(`  ${event.detail}`);
}
```

### Inspect what every agent is working on

Pull active claims and per-member activity for a project to see live agent work at a glance.

```typescript
const { data: activity } = await r.projectBrain.teamActivity('proj_123');

for (const member of activity.team) {
  console.log(`${member.name}: ${member.completed_count} done, ${member.active_claim_count} active`);
}

for (const claim of activity.active_claims) {
  console.log(`In progress: ${claim.task_title} (claimed by ${claim.agent_id})`);
}
```

### Read an agent's inbox as an audit surface

Agent-to-agent delegations, results and status updates are all recorded in the inbox. Read it to reconstruct a chain of delegated work.

```typescript
const { data: messages } = await r.agents.inbox('agent_123', { limit: 100 });

for (const msg of messages) {
  console.log(`${msg.type} from ${msg.fromAgentId}: ${msg.content}`);
}
```

### Track outcomes against a task

Record before/after measurements on a task so the result of agent work is verifiable, not just asserted.

```typescript
await r.dispatcher.recordOutcome('task_123', {
  metric_name: 'p95_latency_ms',
  before_value: '820',
  after_value: '310',
  notes: 'Added the missing index on orders.created_at.',
});

const { data: outcomes } = await r.dispatcher.taskOutcomes('task_123');
console.log(outcomes);
```

### Self-evaluation and recursion (mechanism)

The core pattern is that an agent reviews its own output and runs again until it meets a bar, rather than one-shotting. Today you implement the loop yourself: chat, judge the result (a second agent or a rubric prompt works well) and re-run if it falls short.

```typescript
let { data: result } = await r.agents.chat('agent_writer', { message: task });

for (let i = 0; i < 3; i++) {
  const { data: critique } = await r.agents.chat('agent_reviewer', {
    message: `Score this 1-10 and list fixes. Reply PASS if it is 8+.\n\n${result.content}`,
  });
  if (critique.content.includes('PASS')) break;
  ({ data: result } = await r.agents.chat('agent_writer', {
    message: `Revise based on this feedback:\n${critique.content}`,
    conversation_id: result.conversation_id,
  }));
}
```

A first-class self-evaluation primitive (a scored, structured critique loop you do not have to hand-roll) is on the roadmap. Until it ships, the loop above is the supported approach. There is no `r.verify` resource.