Decisions
Ask a studio member to pick one of your options, then poll the decision until it is answered.
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.
/v1/decisions- Auth
- Bearer
- Scope
- decisions:write
/v1/decisions/:decisionId- Auth
- Bearer
- Scope
- decisions:read
/v1/decisions/:decisionId/withdraw- Auth
- Bearer
- Scope
- decisions:write
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
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.
titlestringrequiredShort name shown in MMA.askstringrequiredThe question, in plain words.projectIdstringoptionalThe project the ask is about. Omit for a studio-wide ask.experimentIdstringoptionalThe experiment the ask is about. It must belong to `projectId` when both are sent.recommendationstringoptionalWhat you recommend and why.optionsarrayoptionalThe 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 `["approve", "deny"]`.costUsdnumberoptionalWhat approving costs, in US dollars, to the cent.dueAtstringoptionalWhen you need the answer, ISO-8601 UTC.requestedByLabelstringoptionalWho asks, e.g. the agent or session name.
Send an Idempotency-Key header.
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"
}'{
"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"
}
}Poll a decision
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
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.
{
"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
POST /v1/decisions/:decisionId/withdraw takes the ask back while it is pending. A decision that is already answered returns 409 CONFLICT.
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. |