> ## Documentation Index
> Fetch the complete documentation index at: https://agentheya.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# confirm_action

> Ask the user to approve a destructive action. Paired with commit_action.

# confirm\_action

Renders a confirm/cancel card asking the user to approve a destructive action. Returns immediately with an `interaction_id`; the user's click arrives on the next turn via interaction replay.

This is the **anti-hallucination cornerstone** for destructive operations. The flow is:

1. LLM calls `confirm_action({ ..., commit_token })` — gets back an `interaction_id`, **not** a result.
2. User clicks Yes/No in the card; the server persists the choice.
3. On the next turn, the tool's original result is rewritten to `{ status: 'submitted' | 'cancelled' | 'expired', result: { confirmed: true|false } }`.
4. Before the destructive call, the LLM must call [commit\_action](/builtin-tools/commit_action) with the same `commit_token`. Only then is the destructive call allowed.

A misbehaving LLM cannot bypass step 4 — `commit_action` is server-validated against persisted approval.

## Parameters

| Name           | Type                           | Required | Description                                                                                                                  |
| -------------- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `title`        | string (1-120)                 | yes      | Short title shown at the top of the confirmation card.                                                                       |
| `description`  | string (1-2000)                | yes      | Plain-language description of EXACTLY what will happen. Be specific.                                                         |
| `action_label` | string (1-40)                  | no       | Text on the confirm button. Default `"Confirm"`. Use a verb.                                                                 |
| `cancel_label` | string (1-40)                  | no       | Text on the cancel button. Default `"Cancel"`.                                                                               |
| `destructive`  | boolean                        | no       | When `true`, the confirm button is styled red. Default `true`.                                                               |
| `commit_token` | string (8-64, `[a-zA-Z0-9_-]`) | yes      | Caller-chosen identifier for THIS action. Same value must be passed to `commit_action`. Don't reuse across distinct actions. |
| `ttl_minutes`  | integer (1-180)                | no       | How long the card stays valid. Default 30.                                                                                   |

## Returns

```json theme={null}
{
  "action": "request_confirm",
  "interaction_id": "...",
  "commit_token": "cancel_order_4231",
  "expires_at": "2026-05-18T10:30:00.000Z",
  "status": "pending",
  "display": { "title": "...", "description": "...", ... }
}
```

## Call from your own custom tool

The cleanest pattern is a single tool that handles both turns. On turn 1 it asks; on turn 2 (after the user clicks) it gates and executes.

```js theme={null}
// cancel_order.js
export async function handle(input, ctx) {
  const token = `cancel_order_${input.order_id}`;

  // Turn 2: try the gate first. If the user has already confirmed, proceed.
  const approval = await ctx.tools.commit_action({
    commit_token: token,
    side_effect: `cancel order #${input.order_id}, refund $${input.amount}`,
  });

  if (approval.ok) {
    // Gate passed — the user clicked Confirm. Do the real work.
    const res = await fetch(
      `https://api.example.com/orders/${input.order_id}/cancel`,
      { method: 'POST' }
    );
    const data = await res.json();
    return { cancelled: true, refund_id: data.refund_id, amount: input.amount };
  }

  // Turn 1: no prior approval exists yet — show the confirmation card.
  await ctx.tools.confirm_action({
    title: 'Cancel order?',
    description: `This will cancel order #${input.order_id} and issue a $${input.amount} refund. This cannot be undone.`,
    action_label: 'Cancel order',
    cancel_label: 'Keep order',
    destructive: true,
    commit_token: token,
  });
  // Returns immediately. The UI shows the card; the LLM waits.
  // When the user clicks, the server persists the choice.
  // The LLM then calls this tool again — and hits the approval.ok branch above.
  return { asked: true };
}
```

### What the user sees

```
┌──────────────────────────────────────────┐
│ Cancel order?                            │
│                                          │
│ This will cancel order #4231 and issue   │
│ a $49.00 refund. This cannot be undone.  │
│                                          │
│  [ Keep order ]      [ Cancel order ]    │  ← red button (destructive: true)
└──────────────────────────────────────────┘
```

### Why the gate cannot be skipped

`commit_action` is validated on the server: it only returns `ok: true` after the user has actually clicked Confirm in the card. The LLM cannot create or fake that approval — so calling `commit_action` before showing the card always fails.

## Notes

* Always registered.
* Scoped to the calling subscriber (or owner in preview).
* The card is rendered automatically by the chat UI and the deployed widget — no UI changes required from your tool.
