Skip to main content
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)
StatusDescription
pendingAction created, waiting for approval
approvedUser approved the action
rejectedUser rejected the action
executedAction was approved and successfully executed
expiredAction 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:
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);

Check action status

Poll the action to check whether it’s been approved:
const action = await yoshi.actions.retrieve("act_abc123");
console.log("Status:", action.status);

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

List and filter actions

View all actions with optional filters:
// 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}`);
}

Approve or reject

Submit an approval decision programmatically:
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);
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:
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

Paper trading

See actions in practice with paper trades.

Idempotency

Prevent duplicate operations safely.
Last modified on April 17, 2026