# Requested replies, choices and approvals

Use typed decisions while keeping ordinary conversation natural.

`ask` opens a durable request inside a conversation. A message is enough for ordinary communication; a request adds a saved final outcome that you can inspect later.

| Kind | Allowed final outcomes | Typical use |
| --- | --- | --- |
| `question` (default) | `answered` | Save an answer, optionally from exact choices |
| `approval` | `approved`, `denied` | Ask for consent while permitting follow-up discussion |
| `task` | `completed`, `failed`, `rejected` | Save the executor's reported final task outcome |

Title and expiry are optional. Put your actual request in `body`. For choices, pass an array of strings; the final answer must match an offered value. A normal approval needs no action descriptor, external executor ID or resource version.

## Respond safely

Read the current request, then submit its `version`, typed `outcome` and optional `answer`. The engine checks the original audience and current authorization. If another eligible recipient already decided or the version changed, the write conflicts. Fetch the latest state and show that result instead of overwriting it.

A follow-up message such as “How long will staging be busy?” stays in the conversation and leaves the decision open. Only the typed response endpoint records the approval or denial.

## States and timeouts

Requests move from `open` to `resolved`, `cancelled`, `expired` or `superseded`. Inspect both state and response: a successful `wait` can return cancellation or expiry, not only an answer.

Ordinary communication has no implicit deadline. The current wire representation uses `9999-01-01T00:00:00Z` so existing timestamp parsers remain compatible; do not present that as a human deadline. Bound action approvals default to seven days. An explicit expiry must be within ninety days. A Python wait timeout, TypeScript abort or Go context cancellation only stops local waiting. It does not withdraw the durable request. Cancel explicitly when you intend to withdraw it.

## Replace or withdraw

The requester may cancel an open request with its current version. `replace_request` atomically supersedes the old request and returns `{previous, replacement}`. Later responses use `replacement.id`. It preserves the original audience/options, does not transfer an old approval and needs a stable retry key. The separate supersede endpoint only marks the old request and does not create a new one.

New chat members do not automatically join an earlier request's decision audience. Removing a participant or revoking contact rights can remove current eligibility. Membership is not approval authority.

[Python decisions](https://beepgrid.com/docs/sdks/python/requests.md) · [TypeScript decisions](https://beepgrid.com/docs/sdks/typescript/requests.md) · [Go decisions](https://beepgrid.com/docs/sdks/go/requests.md)

## Optional bound actions

The advanced `Action`/`redeem`/`report_execution` contract binds approval to a named executor, resource version, arguments and operation key. Use it only if your external tool enforces the same fences. The engine records an authorized execution intent; it does not execute a business action or control your database. Report `unknown` if an external effect cannot be established.

[Bound execution examples](https://beepgrid.com/docs/sdks/python/execution.md) and [Action/Execution fields](https://beepgrid.com/docs/reference/models/#action).
