---
title: "Ask an LLM for the next affiliate-link improvement of one component"
method: POST
path: "/api/admin/affiliate-suggest"
tags: ["admin-affiliate"]
---

# Ask an LLM for the next affiliate-link improvement of one component

`POST /api/admin/affiliate-suggest`

## Request

- Base URL: `https://voltplan.app`
- URL: `https://voltplan.app/api/admin/affiliate-suggest`
- Auth: HTTP bearer

Sends `component` and `stats` to the Anthropic Messages API (`ANTHROPIC_API_KEY` required and checked before the body is read; model `EXTRACT_MODEL`, default `claude-haiku-4-5-20251001`; `max_tokens` 1200) and normalises the returned JSON. Nothing is persisted; apply the result with `POST /api/admin/affiliate-apply`.

- An unknown `actionType` becomes `manual_review`; `reasoning` keeps at most 4 strings.
- `use_existing_url` without `preferredUrl`, and `search_and_apply` or `keep_search_fallback` without `searchQuery`, are downgraded to `manual_review` with `canAutoApply: false`.
- For `search_and_apply` the server calls `POST /api/admin/search-amazon` on its own origin (forwarding the `Authorization` header) and puts the top hit into `researchedUrl`; without a hit it downgrades to `manual_review` and appends a reasoning line.

## Request body

- AdminAffiliateSuggestRequest
  - `component` AdminAffiliateSuggestComponent, required
    - `id` string, required: Id of the `components` record.
    - `name` string, required
    - `type` string
  - `stats` AdminAffiliateSuggestStats, required: Click statistics computed by the admin UI, forwarded verbatim into the prompt; the server only checks that `stats` is present.
    - `clicks` integer
    - `directClicks` integer
    - `searchClicks` integer
    - `externalClicks` integer
    - `hasDirect` boolean
    - `onlySearch` boolean
    - `duplicateUrlCount` integer
    - `uniqueAsinCount` integer
    - `urlCount` integer
    - `topCountries` AdminAffiliateSuggestCountryClicks[]
      - `country` string, required
      - `clicks` integer, required
    - `topUrls` AdminAffiliateSuggestUrlClicks[]
      - `url` string, required
      - `kind` 'direct' | 'search' | 'external', required
      - `clicks` integer, required
      - `asin` string, nullable, required

## Response `200`

Normalised suggestion.

- AdminAffiliateSuggestion: Optional fields are omitted when unset.
  - `actionType` 'use_existing_url' | 'search_and_apply' | 'keep_search_fallback' | 'keep_external' | 'manual_review', required
  - `canAutoApply` boolean, required
  - `summary` string, required: Server fallback text when the model omits it.
  - `primaryAction` string, required: Server fallback text when the model omits it.
  - `searchQuery` string: amazon.de search query.
  - `preferredUrl` string: Clicked URL to promote; never set for `search_and_apply`.
  - `researchedUrl` string: Top `search-amazon` hit; set only for `search_and_apply`.
  - `reasoning` string[], required

## Other responses

- `400`: `component.id`, `component.name` or `stats` is missing.
- `403`: Admin access required. Returned for a missing, invalid or expired bearer token as well as for a valid token whose user lacks `is_admin=true`; admin endpoints never answer 401.
- `500`: `ANTHROPIC_API_KEY` not configured, Anthropic API error (`Claude API error: <status> …`), unparsable model output, or a malformed JSON body; `error` carries the message.

---

[API](https://skmtc.dev/voltplan/apis/voltplan-api.md) · [All operations](https://skmtc.dev/voltplan/apis/voltplan-api/llms.txt) · [OpenAPI document](https://skmtc.dev/voltplan/apis/voltplan-api/revisions/575b86a7af92?raw)
