---
title: Action confirmation
description: Typed confirmation handles for side effects — pending, approving, terminal.
---

# Typed action confirmation

Selection **proposes** a tool call; it never executes. React / RN / Ink render AgentsKit confirmation UI. Manual hosts use `createActionConfirmation`.

## Status machine

<Mermaid
  chart={
    'flowchart LR\n' +
      '  pending --> approving\n' +
      '  pending --> rejecting\n' +
      '  pending --> expiring\n' +
      '  approving --> approved\n' +
      '  rejecting --> rejected\n' +
      '  expiring --> expired'
  }
/>

Without persistent storage, a choice moves `pending` → terminal (`approved` / `rejected` / `expired`) after upstream success.

With [sessions](/docs/sessions), resolution first claims `approving` / `rejecting` / `expiring` via CAS, only the winning client delegates, then the terminal status is persisted.

## Fields

| Field | Meaning |
| --- | --- |
| `token` | Opaque session correlation handle — **not** authentication |
| `sessionId` | Session allowed to approve or reject |
| `action` | Registered upstream tool name |
| `input` | Validated, immutable argument snapshot |
| `toolCallId` | Canonical AgentsKit tool-call identity |
| `expiresAt` | Absolute expiry checked before approval |
| `status` | `pending` · processing · terminal |

## Example

```ts
import { createActionConfirmation } from '@agentskit/chat'

// chat must expose proposeToolCall / approve / deny (e.g. from the controlled driver)
const confirmation = createActionConfirmation({
  sessionId: session.sessionId,
  chat: {
    proposeToolCall: chat.proposeToolCall,
    approve: chat.approve,
    deny: chat.deny,
  },
})

// Propose a tool call — returns a pending record with token + toolCallId
const record = await confirmation.propose({
  name: 'email.send',
  input: { to: 'ada@example.com' },
})

// UI shows confirm/deny bound to record.token
await confirmation.approve(record.token, session.sessionId)
// or: await confirmation.reject(record.token, session.sessionId, 'user denied')
```

### Invariants

- Concurrent replay cannot execute twice (CAS claim before delegation).
- Token from another session is inert.
- Processing + terminal records stay inert on resume after crash.
- This is **not** an audit ledger — attach approver identity and persist audit in the host if needed.

## Related

- [Action policy](/docs/actions/policy)
- [Sessions](/docs/sessions)
- [Components gallery](/docs/examples/components)
