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

# POST /v1/agents/plans/{plan_id}/approve

> Approve a plan waiting for approval, with optional edits to its steps.

Approves a plan that is `awaiting_approval`, exactly as **Approve** on the panel does: what you approve is what runs. You can change a step first — a message's wording, a note, an amount — in the fields its `editable` lists; each edit is checked again as if the agent had planned it. Steps that were due run now; later ones when they fall due.

<Note>Needs the `agents.plans:write` scope.</Note>

## Path parameters

| Field | Type | Required | Notes |
| :- | :- | :- | :- |
| `plan_id` | integer | Yes | The plan's id. |

## Body

| Field | Type | Required | Notes |
| :- | :- | :- | :- |
| `steps` | array | No | Edits: `{ "id": <step id>, <field>: <new value> }` for steps still `pending`. Fields a step doesn't list in `editable` are ignored. |
| `note` | string | No | A note kept with the decision. Up to 1,000 characters. |

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.fireflo.au/v1/agents/plans/4812/approve" \
    -H "Authorization: Bearer $OMNI_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: approve-4812" \
    -d '{
      "steps": [
        {
          "id": 20931,
          "body": "Hi Neha, for 40 units we can do ₹6,350 each, delivered to Ludhiana within 10 days. Shall I send the revised quote?"
        }
      ],
      "note": "Fine — keep delivery at 10 days."
    }'
  ```

  ```python Python theme={null}
  import os

  import requests

  response = requests.post(
      "https://api.fireflo.au/v1/agents/plans/4812/approve",
      headers={
          "Authorization": f"Bearer {os.environ['OMNI_API_KEY']}",
          "Idempotency-Key": "approve-4812",
      },
      json={
          "steps": [
              {
                  "id": 20931,
                  "body": "Hi Neha, for 40 units we can do ₹6,350 each, delivered to Ludhiana within 10 days. Shall I send the revised quote?"
              }
          ],
          "note": "Fine — keep delivery at 10 days."
      },
  )
  print(response.status_code, response.json())
  ```

  ```javascript Node theme={null}
  const response = await fetch("https://api.fireflo.au/v1/agents/plans/4812/approve", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.OMNI_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": "approve-4812",
    },
    body: JSON.stringify({
      "steps": [
        {
          "id": 20931,
          "body": "Hi Neha, for 40 units we can do ₹6,350 each, delivered to Ludhiana within 10 days. Shall I send the revised quote?"
        }
      ],
      "note": "Fine — keep delivery at 10 days."
    }),
  });
  console.log(response.status, await response.json());
  ```
</CodeGroup>

## Response

`200 OK` — the plan, now `scheduled`, with your edits.

```json theme={null}
{
  "id": 4812,
  "agent": {
    "id": 7,
    "name": "Arjun",
    "role": "sales"
  },
  "scope": "contact",
  "campaign_id": null,
  "contact": {
    "id": 90417,
    "uuid": "3f6b2a1e-8c4d-4e2a-9b7f-5d1c0e8a9f42",
    "name": "Neha Arora",
    "msisdn": "+919876500102"
  },
  "deal": {
    "id": 3318,
    "title": "Arora Traders — 40 inverters",
    "stage": "Quoted"
  },
  "pipeline": {
    "id": 2,
    "name": "B2B sales"
  },
  "trigger": "inbound_message",
  "trigger_excerpt": "Can you do ₹6,200 per unit if we take 40?",
  "goal": "Answer Neha's price question and keep the quote moving",
  "rationale": "She asked for a lower unit price on a large order. The playbook allows up to 5% off above 25 units, so offer ₹6,350 and note it on the deal.",
  "status": "scheduled",
  "approval_mode": "plan",
  "approval_reasons": [
    "Send a message needs approval"
  ],
  "created": "2026-10-06T10:14:22+05:30",
  "expires_at": null,
  "next_run_at": "2026-10-06T10:21:05+05:30",
  "tokens": 2310,
  "model": "gpt-4o-mini",
  "decided_by": null,
  "decision_note": "Fine — keep delivery at 10 days.",
  "last_error": null,
  "steps": [
    {
      "id": 20931,
      "order": 1,
      "action": "send_message",
      "title": "Send WhatsApp message",
      "body": "Hi Neha, for 40 units we can do ₹6,350 each, delivered to Ludhiana within 10 days. Shall I send the revised quote?",
      "detail": "WhatsApp",
      "reason": "Answer the price question while she is online.",
      "run_at": "2026-10-06T10:21:05+05:30",
      "requires_approval": true,
      "status": "scheduled",
      "result": null,
      "editable": [
        "body"
      ],
      "params": {
        "channel": "whatsapp",
        "body": "Hi Neha, for 40 units we can do ₹6,350 each, delivered to Ludhiana within 10 days. Shall I send the revised quote?",
        "channel_locked": true
      }
    },
    {
      "id": 20932,
      "order": 2,
      "action": "add_note",
      "title": "Add note",
      "body": "Offered ₹6,350/unit for 40 units (5% off list).",
      "detail": null,
      "reason": "Keep the team aware of the offer.",
      "run_at": "2026-10-06T10:21:05+05:30",
      "requires_approval": false,
      "status": "scheduled",
      "result": null,
      "editable": [
        "body"
      ],
      "params": {
        "body": "Offered ₹6,350/unit for 40 units (5% off list)."
      }
    },
    {
      "id": 20933,
      "order": 3,
      "action": "follow_up",
      "title": "Follow up",
      "body": null,
      "detail": "Check whether Neha accepts the revised price",
      "reason": "No reply by tomorrow means the quote needs a nudge.",
      "run_at": "2026-10-07T11:00:00+05:30",
      "requires_approval": false,
      "status": "scheduled",
      "result": null,
      "editable": [],
      "params": {
        "goal": "Check whether Neha accepts the revised price"
      }
    }
  ],
  "audience": null,
  "story_id": null
}
```

## Errors

Every refusal is `{"error": {"code", "message", "field"}}`; `field` is there when one input is at fault.

| Status | Error code | When |
| :- | :- | :- |
| 400 | `invalid_request` | `steps` isn't a list, or `note` is too long; `field` names it. |
| 404 | `not_found` | No plan of this account's has that id. |
| 409 | `invalid_state` | The plan isn't waiting for approval, or an edit isn't allowed — the message names the step. |
| 404 | `module_off` | The account doesn't have AI agents on: they aren't in its plan, or are switched off for it. |
| 403 | `scope_missing` | The key doesn't have the `agents.plans:write` scope. |

Any request can also be refused for its key, its account or its rate (`key_required`, `invalid_key`, `account_suspended`, `plan_excludes_api`, `address_not_allowed`, `rate_limited`); see [the overview](/api-reference/overview).


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