# Decisions (/docs/api/reference/command-center/decisions)



<Callout type="warn">
  **Deprecated (2026-09-27).** MMA's Decisions screen now leads with MMA's own recommendations: optimizer changes waiting for approval and broken measurement signals, each with its fix. Decisions your agent files appear in a secondary **From your agents** section below them. These endpoints keep working with no change in behavior, and no removal date is set. Do not build new integrations on them.
</Callout>

<Endpoint method="POST" path="/v1/decisions" auth="Bearer" scope="decisions:write" phase="1" />

<Endpoint method="GET" path="/v1/decisions/:decisionId" auth="Bearer" scope="decisions:read" phase="1" />

<Endpoint method="POST" path="/v1/decisions/:decisionId/withdraw" auth="Bearer" scope="decisions:write" phase="1" />

A decision is a question your agent needs a person to answer: raise a budget, ship an experiment's winner, pause a campaign. It appears in MMA with your recommendation and cost, and each of your `options` is one answer button. A studio member picks one, with an optional note, and your agent reads the pick back by polling: `chosenOption` is the option exactly as you sent it.

## Request a decision [#request-a-decision]

A key limited to some apps must name the app with `projectId` or `experimentId` when it files an ask. Filing for another app returns `403 FORBIDDEN_SCOPE`. Reading or withdrawing a decision on another app returns `404 NOT_FOUND`.

<Parameters
  title="Body"
  rows="[
  { name: 'title', type: 'string', required: true, description: 'Short name shown in MMA.' },
  { name: 'ask', type: 'string', required: true, description: 'The question, in plain words.' },
  { name: 'projectId', type: 'string', description: 'The project the ask is about. Omit for a studio-wide ask.' },
  { name: 'experimentId', type: 'string', description: 'The experiment the ask is about. It must belong to `projectId` when both are sent.' },
  { name: 'recommendation', type: 'string', description: 'What you recommend and why.' },
  { name: 'options', type: 'array', description: 'The answers offered, 1 to 10 unique strings of up to 100 characters. Each is one button in MMA, and the one picked comes back verbatim as `chosenOption`. Defaults to `[&#x22;approve&#x22;, &#x22;deny&#x22;]`.' },
  { name: 'costUsd', type: 'number', description: 'What approving costs, in US dollars, to the cent.' },
  { name: 'dueAt', type: 'string', description: 'When you need the answer, ISO-8601 UTC.' },
  { name: 'requestedByLabel', type: 'string', description: 'Who asks, e.g. the agent or session name.' },
]"
/>

Send an `Idempotency-Key` header.

```bash
curl -X POST "https://api.layers.com/v1/decisions" \
  -H "Authorization: Bearer lp_..." \
  -H "Idempotency-Key: catch-master-budget-2026-09-26" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
    "title": "Raise Meta daily budget to $400",
    "ask": "D7 ROAS held at 1.4 for 5 days at $250 a day. Raise the daily budget to $400?",
    "recommendation": "Approve. The last two raises kept CPI within 8%.",
    "costUsd": 1050,
    "requestedByLabel": "catch-master-growth-agent"
  }'
```

<Response status="201" description="Created">
  ```json
  {
    "decision": {
      "id": "dec_7a2e9c41-0b6d-4f3e-8c15-9d4b2a6e1f07",
      "projectId": "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
      "experimentId": null,
      "title": "Raise Meta daily budget to $400",
      "ask": "D7 ROAS held at 1.4 for 5 days at $250 a day. Raise the daily budget to $400?",
      "recommendation": "Approve. The last two raises kept CPI within 8%.",
      "options": ["approve", "deny"],
      "costUsd": 1050,
      "dueAt": null,
      "status": "pending",
      "chosenOption": null,
      "decisionNote": null,
      "decidedAt": null,
      "requestedByLabel": "catch-master-growth-agent",
      "createdAt": "2026-09-26T14:05:00Z",
      "updatedAt": "2026-09-26T14:05:00Z"
    }
  }
  ```
</Response>

## Poll a decision [#poll-a-decision]

```bash
curl "https://api.layers.com/v1/decisions/dec_7a2e9c41-0b6d-4f3e-8c15-9d4b2a6e1f07" \
  -H "Authorization: Bearer lp_..."
```

| `status`    | Meaning                                                                               | Your next action                           |
| ----------- | ------------------------------------------------------------------------------------- | ------------------------------------------ |
| `pending`   | Waiting for a studio member.                                                          | Poll again; a few minutes apart is enough. |
| `approved`  | The member picked `approve`. `chosenOption` is `"approve"`.                           | Act on it. Stop polling.                   |
| `denied`    | The member picked `deny`. `chosenOption` is `"deny"`.                                 | Drop the ask. Stop polling.                |
| `answered`  | The member picked one of your other options. `chosenOption` is that option, verbatim. | Act on the option picked. Stop polling.    |
| `withdrawn` | You withdrew it. `chosenOption` is `null`.                                            | Stop polling.                              |

In every answered status `decisionNote` carries the member's reason when they gave one. Read `chosenOption` to learn the answer; it works the same for the default pair and for your own options.

## Offer your own options [#offer-your-own-options]

Send `options` when approve or deny is not the question. Each string is a button in MMA and the value you read back, so write them as the action you will take.

```json
{
  "projectId": "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  "title": "Paywall test: arm B is ahead",
  "ask": "Arm B leads trial starts by 4.2 points, interval 1.1 to 7.3. What next?",
  "options": ["ship B", "extend a week", "stop the test"]
}
```

A member who picks "extend a week" closes the decision with `status: "answered"` and `chosenOption: "extend a week"`. Options that include `approve` or `deny` keep their verdict: picking `approve` from `["approve", "approve at half budget", "deny"]` returns `status: "approved"`.

## Withdraw a decision [#withdraw-a-decision]

`POST /v1/decisions/:decisionId/withdraw` takes the ask back while it is `pending`. A decision that is already answered returns `409 CONFLICT`.

## Errors [#errors]

| Status | Code         | When                                                                                        |
| ------ | ------------ | ------------------------------------------------------------------------------------------- |
| `404`  | `NOT_FOUND`  | The decision, project or experiment is not in your organization.                            |
| `409`  | `CONFLICT`   | Withdrawing a decision that is already answered or withdrawn.                               |
| `422`  | `VALIDATION` | A malformed field, an extra field, an experiment on a different project, or a malformed id. |
