Add alignment suggestion
Stages one hand-written alignment prompt into an existing generation run, so it sits alongside the AI-generated suggestions in that batch and is decided through the same accept, reject and edit endpoints. The run must already exist — start one with Generate alignment suggestions first.
Provide category_id to target an existing category, or category_name to propose a brand-new
one — it is only materialized into a real category if the suggestion is later accepted. Provide
neither to leave it uncategorised. Never provide both.
Requires the write:alignment scope, and the workspace must be enabled for API writes.
POST /v1/brands/{brand_id}/alignment/suggestionsAuthorization
| Header | Type | Required |
|---|---|---|
Authorization | Bearer asky_sk_… | required |
Idempotency-Key | string | required |
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
brand_id | uuid | required | From List brands |
Body
application/json
| Field | Type | Required | Description |
|---|---|---|---|
request_id | uuid | required | The generation batch to add into, from Generate alignment suggestions |
suggested_prompt | string, 1-500 chars | required | |
country_codes | string[], 1-20 | required | Markets for the prompt |
category_id | uuid | optional | An existing category, from List alignment categories. Mutually exclusive with category_name |
category_name | string, up to 100 chars | optional | Propose a brand-new category. Mutually exclusive with category_id |
category_slug | string, up to 100 chars | optional | Only used alongside category_name; derived from it if omitted |
Request
cURL
curl --request POST \
--url 'https://api.askylabs.com/v1/brands/242546de-4cee-4a7d-952e-f6aa60c63ef8/alignment/suggestions' \
--header 'Authorization: Bearer <api-key>' \
--header 'Idempotency-Key: 5b0e77d2-13a9-4f6a-8ce1-9a3f5e21c0aa' \
--header 'Content-Type: application/json' \
--data '{"request_id": "7f3ab21c-9e4d-4b8a-8f1a-2c9d6e4b7a30", "suggested_prompt": "Does this brand publish a signed sustainability report?", "country_codes": ["US"], "category_name": "Sustainability claims"}'Response
200
{
"data": {
"suggestion": {
"id": "e4a2c8f0-6b1d-4a9e-9c3f-1d8b5a2e7f60",
"brand_id": "242546de-4cee-4a7d-952e-f6aa60c63ef8",
"workspace_id": "6b2e0a94-7c1f-4b3d-9e8a-1a2c9d6e4b70",
"request_id": "7f3ab21c-9e4d-4b8a-8f1a-2c9d6e4b7a30",
"suggested_prompt": "Does this brand publish a signed sustainability report?",
"language": "en",
"country_codes": ["US"],
"proposed_category_name": "Sustainability claims",
"proposed_category_slug": "sustainability-claims",
"proposed_category_description": null,
"category_id": null,
"agent_reasoning": null,
"status": "proposed",
"promoted_to_prompt_id": null,
"rejected_reason": null,
"created_at": "2026-08-30T09:20:11.402Z"
}
},
"request_id": "req_7d21b0af59c34e18b6ac"
}Response fields
| Field | Type | Description |
|---|---|---|
data.suggestion.id | uuid | required. Use it with Accept / Reject / Edit alignment suggestions |
data.suggestion.brand_id | uuid | required |
data.suggestion.workspace_id | uuid | required |
data.suggestion.request_id | uuid | required. The generation batch this was added into |
data.suggestion.suggested_prompt | string | required |
data.suggestion.language | string | required. Inherited from the run, else the brand’s prompt language, else en — not settable on this endpoint |
data.suggestion.country_codes | string[] | required |
data.suggestion.proposed_category_name | string | null | Echoes category_name when given |
data.suggestion.proposed_category_slug | string | null | The slug the new category will get if accepted |
data.suggestion.proposed_category_description | string | null | |
data.suggestion.category_id | uuid | null | Echoes category_id when given |
data.suggestion.agent_reasoning | string | null | Always null here — reasoning is only set for AI-generated suggestions |
data.suggestion.status | string | required. Always proposed |
data.suggestion.promoted_to_prompt_id | uuid | null | Always null here |
data.suggestion.rejected_reason | string | null | Always null here |
data.suggestion.created_at | timestamptz | required |
request_id | string | required. Also returned as the x-request-id header. Quote it when contacting support |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | Unknown field, a missing required field, both category_id and category_name provided, category_id not found for this brand, category_name did not resolve to a valid slug, or this run already holds the maximum number of suggestions |
| 401 | invalid_token | The key is missing, malformed, or does not exist |
| 401 | token_expired | The key passed its expiry date |
| 401 | token_revoked | The key was revoked |
| 403 | insufficient_scope | The key does not carry write:alignment |
| 403 | plan_required | This workspace is not enabled for API writes |
| 404 | not_found | The brand does not exist, this key is not allowed to reach it, or request_id does not exist for this brand |
| 409 | conflict | suggested_prompt already exists in this generation run, or this brand already tracks an active alignment prompt with that text |
| 429 | rate_limited | Over 60 requests per minute |
Notes
category_id and category_name are mutually exclusive. Sending both refuses the call.
Sending neither leaves the suggestion uncategorised.
A proposed category is not created yet. category_name only stages a proposal —
Accept alignment suggestions is what resolves it to
an existing category with the same slug or creates a new one, the first time a suggestion using
that slug is accepted. An abandoned run never leaves an orphan category behind.
category_slug only matters with category_name. It is ignored if category_id is set, and
derived from category_name if omitted.
Duplicate detection runs against both the run and live prompts. 409 conflict fires if
suggested_prompt normalizes to the same text as another non-rejected suggestion already in this
run, or as an existing active alignment prompt on this brand.