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

# Recommendations

> List, transition, generate, and draft recommendations.

Recommendations are prioritized action items for improving your brand's GEO and SEO performance. They have a lifecycle with CAS-guarded transitions.

## Endpoints

| Method | Path                                                  | Scope        | Description                            |
| ------ | ----------------------------------------------------- | ------------ | -------------------------------------- |
| GET    | `/api/v1/workspaces/{workspace}/recs`                 | `recs:read`  | List recommendations grouped by status |
| POST   | `/api/v1/workspaces/{workspace}/recs/generate`        | `recs:write` | Generate recommendations for a lane    |
| POST   | `/api/v1/workspaces/{workspace}/recs/{id}/transition` | `recs:write` | CAS-guarded status transition          |
| POST   | `/api/v1/workspaces/{workspace}/recs/{id}/status`     | `recs:write` | Deprecated alias for transition        |
| POST   | `/api/v1/workspaces/{workspace}/recs/{id}/draft`      | `recs:write` | Generate a draft comment               |

## Status lifecycle

```text theme={null}
proposed → todo → in_review → done
  ↓          ↓         ↓
archived   archived   archived
```

| Status      | Meaning                              |
| ----------- | ------------------------------------ |
| `proposed`  | New recommendation, not yet accepted |
| `todo`      | Accepted and queued for work         |
| `in_review` | Implementation submitted for review  |
| `done`      | Completed                            |
| `archived`  | Declined or superseded               |

### Supported transitions (action \<expectedStatus>)

| Action            | From                        | To               |
| ----------------- | --------------------------- | ---------------- |
| `accept`          | `proposed`                  | `todo`           |
| `decline`         | `proposed`                  | `archived`       |
| `submit`          | `todo`                      | `in_review`      |
| `verify`          | `in_review`                 | `done`           |
| `request_changes` | `in_review`                 | `todo`           |
| `stale_reopen`    | `done`                      | `todo`           |
| `reopen`          | `archived`                  | `restore_target` |
| `archive`         | `todo`, `in_review`, `done` | `archived`       |
| `restore`         | `archived`                  | `restore_target` |

## List recommendations

```bash theme={null}
curl -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
     https://ai-cmo.dev/api/v1/workspaces/{workspace}/recs
```

Returns recommendations grouped by status (`proposed`, `todo`, `in_review`, `done`, `archived`). Each recommendation includes `rec_id`, `type`, `topic`, `title`, `rationale`, `evidence`, `scores`, `state_version`, `compliance_flag`, and `current_draft_id`.

## Transition a recommendation

The transition endpoint uses compare-and-swap (CAS) on `expectedVersion`. All four fields below are required.

```bash theme={null}
curl -X POST https://ai-cmo.dev/api/v1/workspaces/{workspace}/recs/{id}/transition \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "accept",
    "expectedStatus": "proposed",
    "expectedVersion": 1,
    "idempotencyKey": "transition-recs-01"
  }'
```

### Required fields

<ParamField name="action" type="string" required>
  One of: `accept`, `decline`, `submit`, `verify`, `request_changes`, `stale_reopen`, `reopen`, `archive`, `restore`.
</ParamField>

<ParamField name="expectedStatus" type="string" required>
  Current status you expect the recommendation to be in.
</ParamField>

<ParamField name="expectedVersion" type="integer" required>
  Current `state_version` from the recommendation.
</ParamField>

<ParamField name="idempotencyKey" type="string" required>
  1–200 characters, makes the transition safe for retries.
</ParamField>

### Optional fields

<ParamField name="note" type="string" optional>
  Max 300 characters.
</ParamField>

<ParamField name="reviewEvidence" type="object" optional>
  `{ decision: "approved"|"changes_requested", comment?, reviewedAt }`.
</ParamField>

<ParamField name="visibilitySnapshot" type="object" optional>
  Metrics snapshot with `metric`, `scope`, `value`, `n`, `observedAt`.
</ParamField>

<ParamField name="draftId" type="string" optional>
  Associate with a draft comment.
</ParamField>

<ParamField name="complianceApproved" type="boolean" default="false" optional>
  Whether compliance review has been completed.
</ParamField>

### Stale version conflict

If the recommendation has been modified since you last fetched it, the server returns:

```json theme={null}
{
  "error": {
    "code": "stale_state",
    "message": "Recommendation state version has changed",
    "requestId": "a1b2c3d4"
  }
}
```

Re-fetch the recommendation to get the current `state_version`, then retry.

## Generate recommendations

```bash theme={null}
curl -X POST https://ai-cmo.dev/api/v1/workspaces/{workspace}/recs/generate \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"lane": "seo"}'
```

| Lane  | Cost             | Creates at most | Target open items |
| ----- | ---------------- | --------------- | ----------------- |
| `seo` | Credits consumed | 20              | 50                |
| `geo` | Free             | 10              | 20                |

Returns (202) a job envelope. SEO generation is metered and consumes credits; GEO generation is free.

## Draft a comment

Generates a draft comment for a `reddit_thread` recommendation:

```bash theme={null}
curl -X POST https://ai-cmo.dev/api/v1/workspaces/{workspace}/recs/{id}/draft \
  -H "Authorization: Bearer gp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

Non-reddit recommendations return a zod validation error.
