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

# Action confirmation

> Samples in which a backend asks a person to confirm an action in a messenger: a plain confirmation, step-up before a destructive action and approval of an AI agent's tool call.

Here the backend, not the browser, starts the transaction: it asks a person to confirm an action in
a messenger and acts on the outcome. There is no sign-in and no browser redirect. The three samples
live under `samples/dotnet/inproc/` in the repository ([veriqa.app/source](https://veriqa.app/source))
and are MIT-licensed.

| Sample | The action | What it adds | Address |
| - | - | - | - |
| `confirmation` | "Approve a payment of 42.00 EUR to ACME Ltd?" | The bare mechanism: create, show the channel entry, wait, read the outcome | `https://localhost:7310` |
| `step-up` | Deleting a project, which cannot be undone | Only the **owner of the account** may confirm: `expected_identities` and `matched_type` | `https://localhost:7330` |
| `agent-approval` | A support agent's `refund_payment` tool call | Human-in-the-loop: the tool runs only after a person approves the call | `https://localhost:7340` |

## How each of them works

Every sample is one process in two roles: the Veriqa issuer (embedded) and the backend that calls it
**only over the public HTTP API**. Point the backend at a standalone Veriqa and it works the same.

1. `POST /connect/token` with `grant_type=client_credentials`.
2. `POST /api/transaction/confirmation` with the declared `action_type` and its `slot_values`.
3. The page shows `channel_entry.qr` — the QR image the API returns — for the user to scan with their phone.
4. The backend polls `GET /api/transaction/{id}/result`.
5. On `confirmed` it may exchange the transaction once for the `id_token` of the person who
   confirmed (`grant_type=urn:veriqa:params:oauth:grant-type:confirmation`).

The full contract — fields, errors, the exchange — is in the
[confirmation API reference](/docs/reference/confirmation-api).

## The configuration that makes it work

The backend's client entry in `appsettings.json` carries:

* `AllowClientCredentials: true` — the backend may get a token for the API (needs a `ClientSecret`);
* `AllowConfirmationTokenGrant: true` and `openid` in `AllowedScopes` — step 5;
* `MessageTemplates:<action type>` — the declaration of the action: `Contract:Slots` and `Templates`.
  It belongs in the **client entry**; an action type declared only at the host level is refused with
  `action_type_unknown`;
* for `step-up` also `IdentityMatchComparableTypes`, which declares the identity compared with the
  person who confirmed. Without it the request is refused with `candidate_type_undeclared`.

The host section sets `Veriqa:Channels:OutcomeNotice:DisplayIntent` to `NewMessage`: the receipt
arrives as a message of its own and the question with the wording of the action stays in the
conversation. See `OutcomeNotice` in [Configuration](/docs/reference/configuration).

## Run a sample

Each sample needs a trusted local HTTPS certificate (`dotnet dev-certs https --trust`) and a Telegram
bot token from [@BotFather](https://t.me/BotFather). Pass the token through user secrets, not a
committed file:

```bash theme={null}
dotnet user-secrets --project samples/dotnet/inproc/confirmation set "Veriqa:Channels:Telegram:BotToken" "<token>"
dotnet user-secrets --project samples/dotnet/inproc/confirmation set "Veriqa:Channels:Telegram:Enabled" "true"
dotnet run --project samples/dotnet/inproc/confirmation
```

For `step-up` and `agent-approval` replace the project path. In Development the bot runs in polling
mode, so no public URL is needed. Without an enabled channel, creating a confirmation fails with
`503 channel_display_failed`.

What to try in each:

* **confirmation** — click **Ask for approval**, confirm in the bot: the page shows
  `Outcome: confirmed` and the claims of the `id_token`.
* **step-up** — delete a project: the first person to confirm becomes the owner. Delete another one
  from a different Telegram account: the confirmation succeeds, but the page shows *Not deleted —
  confirmed, but not by the owner*.
* **agent-approval** — click **Hand the ticket to the agent**: the agent stops at `refund_payment`
  until you approve. Decline on a second run: the refund is not executed and the agent tells the
  customer a colleague will contact them. The agent follows a fixed plan, so no model or API key is
  needed.

## See also

* [Second factor, step-up and action approvals](/docs/scenarios/step-up)
* [Approving AI-agent actions](/docs/research/agent-approvals)
* [Video: step-up on a desktop](/docs/videos/desktop-step-up)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.