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

# Murph confirmations

> Handle explicit approval for Murph durable-write actions

Murph can propose durable writes while helping operate a storefront, such as
filing a report, updating operating instructions, publishing storefront setup
state, or changing other persistent configuration. For protected writes, Murph
returns a structured confirmation request instead of executing the write
immediately.

Clients should render the confirmation as an explicit approve/cancel control.
The control exists only when the response contains a structured pending
confirmation; assistant prose cannot create one. For reports, integration
requests, and asks, Murph uses this staged control as the filing offer, and no
team-visible record exists until the approved replay succeeds.
On Murph UI-control surfaces, an ordinary-language reply that clearly approves
the action—such as "yes", "yep", "sounds good", or an equivalent in the user's
language—is also accepted for the exact pending action. Murph interprets the
reply semantically; ambiguous, conditional, or conflicting replies do not
execute. Phrase-token API/MCP flows still require the exact confirmation phrase.

Filing an ask offers the control before anything reaches Apostra team, and it
always does so when the same conversation has already read content Murph did not
author — an uploaded document, a search result, or a response from another agent.
Text from those sources can contain an instruction, and your approval is what
stops such an instruction from putting a request in Apostra queue that looks
like you sent it. Murph also looks into a problem before offering to file it, so
on a broken-or-blocked ask the approval offer arrives after that check rather
than instead of it.

## Confirmation lifecycle

1. Send a normal Murph chat request.
2. If the response includes `pendingConfirmations` (or `pendingConfirmation`),
   show the proposed action and an approve/cancel control for each entry.
3. POST each user decision to the confirmation decision endpoint.
4. If the user confirms, the response is a normal Murph chat response for the
   turn that executed the approved write.
5. If the user cancels, the response only records the cancelled confirmation.

<Note>
  **Prefer `pendingConfirmations` (plural).** Murph can propose multiple gated
  writes in a single turn (e.g. queue N `delete_campaign` calls). Older clients
  that only read `pendingConfirmation` (singular) render just the first widget
  and silently drop the rest. New clients should iterate `pendingConfirmations`
  and render one approve/cancel control per entry; each entry has its own
  `confirmationUid` and is decided independently.
</Note>

<Warning>
  `confirmationUid` is short-lived, single-use, and tied to the authenticated
  actor, conversation, tool, and resolved parameters. Do not persist it as a
  long-term approval record.
</Warning>

## Chat response fields

`POST /api/v2/murph/chat` and `POST /api/v2/murph/chat/stream` can return
two related fields on a turn where one or more protected writes were
proposed but did not run:

* **`pendingConfirmations`** *(array, new)* — every pending confirmation
  issued this turn, in tool-call order. Render one approve/cancel control
  per entry. Empty (or omitted) when nothing gated this turn. **New
  callers should prefer this field.**
* **`pendingConfirmation`** *(object, retained)* — the first entry from
  `pendingConfirmations`, or `null`. Kept for back-compat with clients
  that render at most one confirmation per turn (Slack, older SDKs). A
  client that reads only this field will silently drop the remaining
  N-1 controls when Murph gates multiple writes in one turn.

```json theme={null}
{
  "data": {
    "conversationUid": "6e60d909-721e-41ca-85b8-4e5a72bbf792",
    "answer": "This action needs approval before it runs.",
    "toolsUsed": [],
    "escalated": false,
    "escalation": null,
    "customerSwitch": null,
    "pendingConfirmation": {
      "confirmationUid": "50669fad-52e1-43c8-9334-77bf14ba72eb",
      "toolName": "save_ask",
      "toolKey": "save_ask",
      "summary": "Run `save_ask`.",
      "policy": "always",
      "resolvedParams": {
        "title": "Checkout page error",
        "severity": "low"
      },
      "writeExecuted": false,
      "plainAffirmativeAccepted": true,
      "expiresAt": "2026-06-13T19:45:00.000Z"
    },
    "pendingConfirmations": [
      {
        "confirmationUid": "50669fad-52e1-43c8-9334-77bf14ba72eb",
        "toolName": "save_ask",
        "toolKey": "save_ask",
        "summary": "Run `save_ask`.",
        "policy": "always",
        "resolvedParams": {
          "title": "Checkout page error",
          "severity": "low"
        },
        "writeExecuted": false,
        "plainAffirmativeAccepted": true,
        "expiresAt": "2026-06-13T19:45:00.000Z"
      }
    ]
  }
}
```

Each entry (in either field) uses this shape:

| Field                      | Type                      | Notes                                                                                                                                                                                     |
| -------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `confirmationUid`          | UUID                      | Pass this to the decision endpoint. Each pending confirmation gets its own UID; a multi-write turn yields N distinct UIDs.                                                                |
| `toolName`                 | string                    | Internal Murph tool that requested the write.                                                                                                                                             |
| `toolKey`                  | string                    | Stable key for the gated action.                                                                                                                                                          |
| `summary`                  | string                    | Human-readable action summary for the confirmation UI.                                                                                                                                    |
| `policy`                   | `"always"` or `"tainted"` | Why the confirmation gate applied.                                                                                                                                                        |
| `resolvedParams`           | object                    | Parameters the user is approving.                                                                                                                                                         |
| `writeExecuted`            | `false`                   | The write did not run on this turn.                                                                                                                                                       |
| `plainAffirmativeAccepted` | boolean                   | When `true`, a semantically clear ordinary-language approval can redeem this exact pending UI action; ambiguous or conflicting replies do not execute. Phrase-token flows remain `false`. |
| `expiresAt`                | datetime                  | Confirmation expiry time.                                                                                                                                                                 |

## Confirm or cancel

`POST /api/v2/murph/confirmations/{confirmationUid}/decision`

```bash theme={null}
curl -X POST "https://api.apostra.com/api/v2/murph/confirmations/50669fad-52e1-43c8-9334-77bf14ba72eb/decision" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "decision": "confirm" }'
```

## Request body

| Field      | Type                      | Required | Notes                                  |
| ---------- | ------------------------- | -------- | -------------------------------------- |
| `decision` | `"confirm"` or `"cancel"` | Yes      | Confirms or cancels the pending write. |

## Confirm response

When `decision` is `confirm`, the endpoint returns the same shape as a Murph
chat response. The approved write may appear in `toolsUsed`, and fields such as
`escalated` or `customerSwitch` reflect the write that actually ran.

If the approved tool returns an error, Murph distinguishes outcomes only when
the tool can prove the failure happened before any durable write could take
effect. In that case Murph says that nothing was written and can offer a fresh
approval after the cause is corrected. For timeouts, transport failures, server
errors, and tools that provide no proof, Murph reports the outcome as unknown
and asks you to verify the current state before retrying. This conservative
default prevents an already-applied write from being repeated after its response
was lost.

## TARS PM investment decisions

TARS PM-seat conversations in Admin and ordinary chat use the same confirmation
lifecycle when an accountable human adopts, prioritizes, defers, reorders,
resizes, or declines a displayed investment recommendation. TARS first reads
the canonical recommendation for the selected project or initiative, then
stages `tars_confirm_pm_investment_decision` with the exact proposal,
disposition, target order, and any owned readiness action.

The pending confirmation is bound to the authenticated actor, conversation,
selected project or initiative, and canonical proposal targets. A confirmed
replay uses that stored scope; clients cannot replace it by sending a different
scope or target. The resulting decision records whether it came from Admin or
ordinary chat, but that provenance is resolved by the server rather than
accepted from client input.

A registered Slack investment-recommendation thread records the same canonical
decision semantics through its own server-validated thread and
accountable-human authority. It does not issue or redeem the pending
confirmation described on this page. Slack provenance is likewise resolved by
the server, never supplied as decision input.

Confirming an investment decision does **not** file an issue, add a
`build-room-ready` label, nominate work, request admission, launch a workspace,
or allocate compute. A request to cross one of those boundaries is a distinct
execution transition with its own authority and confirmation requirements.

<Note>
  These PM-seat tools are available only on authenticated internal TARS surfaces.
  They are not general Murph campaign-management tools and do not change the
  account or advertiser scope of a Murph conversation.
</Note>

```json theme={null}
{
  "data": {
    "conversationUid": "6e60d909-721e-41ca-85b8-4e5a72bbf792",
    "answer": "Filed the report.",
    "toolsUsed": [
      {
        "name": "save_ask",
        "input": {
          "title": "Checkout page error",
          "severity": "low"
        },
        "output": "{ \"status\": \"filed\" }",
        "isError": false
      }
    ],
    "escalated": true,
    "escalation": null,
    "customerSwitch": null,
    "pendingConfirmation": null
  }
}
```

## Cancel response

When `decision` is `cancel`, no write runs and the response only records the
cancelled confirmation.

```json theme={null}
{
  "data": {
    "confirmationUid": "50669fad-52e1-43c8-9334-77bf14ba72eb",
    "status": "cancelled"
  }
}
```

## Errors

* `400 VALIDATION_ERROR` - the path or request body is malformed.
* `404 NOT_FOUND` - the confirmation is unknown, expired, cancelled, or already
  used.
* `403 FORBIDDEN` - the confirmation belongs to another actor or account.
* `503 SERVICE_UNAVAILABLE` - Murph is not available to process a confirmed
  write.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Ask Murph" href="/v2/setup/ask-murph" icon="sparkles">
    Use Murph to operate and troubleshoot your storefront.
  </Card>

  <Card title="Authentication" href="/v2/authentication" icon="key">
    Authenticate Murph API requests.
  </Card>
</CardGroup>
