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

# Actions and approvals for safe financial operations

> Create, track, and approve actions that modify financial data. Understand the approval state machine and build approval workflows into your application.

Actions are Yoshi's safety layer for operations that modify financial data. Instead of executing immediately, mutating operations create a **pending action** that must be approved before taking effect.

## Why actions exist

Financial operations — placing trades, creating accounts, transferring funds — carry real consequences. The action model ensures:

* Every mutating operation requires explicit approval
* You can review what will happen before it executes
* Actions are auditable with full history
* Integrations can propose operations that a human approves

## The action lifecycle

```
  create       approve        execute
    │             │              │
    ▼             ▼              ▼
 pending ──→ approved ──→ executed
    │
    └──→ rejected
    │
    └──→ expired (if expires_at passes)
```

| Status     | Description                                   |
| ---------- | --------------------------------------------- |
| `pending`  | Action created, waiting for approval          |
| `approved` | User approved the action                      |
| `rejected` | User rejected the action                      |
| `executed` | Action was approved and successfully executed |
| `expired`  | Action expired before a decision was made     |

## Create an action

Actions are created automatically when you call mutating endpoints like paper trading. You can also create custom actions:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Yoshi from "@yoshi-ai/sdk";

  const yoshi = new Yoshi();

  const action = await yoshi.actions.create({
    action_type: "paper_trade",
    title: "Buy 10 shares of AAPL",
    description: "Paper trade in Tech Portfolio account",
    proposal_data: {
      symbol: "AAPL",
      side: "buy",
      quantity: 10,
    },
    source: "consumer_api",
  });

  console.log("Action ID:", action.id);
  console.log("Approve at:", action.approval_url);
  ```

  ```python Python theme={null}
  from yoshi import Yoshi

  yoshi = Yoshi()

  action = yoshi.actions.create(
      action_type="paper_trade",
      title="Buy 10 shares of AAPL",
      description="Paper trade in Tech Portfolio account",
      proposal_data={
          "symbol": "AAPL",
          "side": "buy",
          "quantity": 10,
      },
      source="consumer_api",
  )

  print(f"Action ID: {action.id}")
  print(f"Approve at: {action.approval_url}")
  ```
</CodeGroup>

## Check action status

Poll the action to check whether it's been approved:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const action = await yoshi.actions.retrieve("act_abc123");
  console.log("Status:", action.status);

  if (action.status === "executed") {
    console.log("Result:", action.execution_result);
  }
  ```

  ```python Python theme={null}
  action = yoshi.actions.retrieve("act_abc123")
  print(f"Status: {action.status}")

  if action.status == "executed":
      print(f"Result: {action.execution_result}")
  ```
</CodeGroup>

## List and filter actions

View all actions with optional filters:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // List pending actions
  for await (const action of yoshi.actions.list({ status: "pending" })) {
    console.log(`${action.title} — ${action.status}`);
  }

  // List by type
  for await (const action of yoshi.actions.list({
    action_type: "paper_trade",
  })) {
    console.log(`${action.title} — ${action.status}`);
  }
  ```

  ```python Python theme={null}
  for action in yoshi.actions.list(status="pending"):
      print(f"{action.title} — {action.status}")
  ```
</CodeGroup>

## Approve or reject

Submit an approval decision programmatically:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await fetch(
    "https://api.yoshi.ai/actions/act_abc123/decide",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.YOSHI_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ decision: "approved" }),
    },
  );
  const { data } = await result.json();
  console.log("Decision:", data.decision, "Status:", data.status);
  ```

  ```python Python theme={null}
  response = httpx.post(
      "https://api.yoshi.ai/actions/act_abc123/decide",
      headers={"Authorization": f"Bearer {os.environ['YOSHI_API_KEY']}"},
      json={"decision": "approved"},
  )
  data = response.json()["data"]
  print(f"Decision: {data['decision']} Status: {data['status']}")
  ```
</CodeGroup>

You can also approve from the Yoshi web app using the `approval_url` returned when the action was created.

## Use idempotency keys

Prevent duplicate actions when retrying:

```bash theme={null}
curl -X POST https://api.yoshi.ai/actions \
  -H "Authorization: Bearer yoshi_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: buy-aapl-2026-04-10" \
  -d '{"action_type": "paper_trade", "title": "Buy AAPL", ...}'
```

If a pending action with the same idempotency key already exists, the API returns the existing action instead of creating a duplicate. A `409 Conflict` is returned if the key matches but the parameters differ.

## What's next

<CardGroup cols={2}>
  <Card title="Paper trading" icon="chart-candlestick" href="/guides/paper-trading">
    See actions in practice with paper trades.
  </Card>

  <Card title="Idempotency" icon="fingerprint" href="/concepts/idempotency">
    Prevent duplicate operations safely.
  </Card>
</CardGroup>
