Create prompt
Starts monitoring a question. The prompt is created, consumes plan allowance, and begins executing immediately across the engines the brand tracks.
Requires the write:prompts scope, and the workspace must be enabled for API writes.
POST /v1/brands/{brand_id}/promptsThis one costs money. Every other write on this API edits configuration. Creating a prompt consumes one active-prompt slot per market and triggers a run against every engine, so a single call produces real usage. Budget for it the way you would a job you queued, not the way you would a config change.
Authorization
| 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 |
Query parameters
None.
Any parameter you pass returns 400 invalid_request naming the offending key.
Body
application/json
| Field | Type | Required | Description |
|---|---|---|---|
text | string, 1–500 chars | required | The question to monitor |
country_codes | string[], up to 20 | optional | Two-letter markets like US, SE. Defaults to the brand’s market. Each one consumes a slot |
topic_id | uuid | null | optional | Omit for uncategorised |
tag_ids | uuid[], up to 50 | optional | Tags to apply to the new prompt |
There is no is_active. Prompts cannot be paused, in the app or here, so every prompt created is
active and counts toward your allowance.
Request
cURL
curl --request POST \
--url 'https://api.askylabs.com/v1/brands/242546de-4cee-4a7d-952e-f6aa60c63ef8/prompts' \
--header 'Authorization: Bearer <api-key>' \
--header 'Idempotency-Key: <unique-per-attempt>' \
--header 'Content-Type: application/json' \
--data '{"text":"What is the best AI visibility tracker?","country_codes":["US","SE"]}'Response
200
{
"data": {
"prompt": {
"id": "b71e3c04-95af-4d28-8a16-5c0f7e2d1a93",
"prompt_text": "What is the best AI visibility tracker?",
"topic_id": null,
"funnel_stages": [],
"is_active": true
},
"country_codes": ["US", "SE"],
"quota": {
"limit": 200,
"used": 148,
"remaining": 52
}
},
"request_id": "req_0f3a71be29c44d85b6ce"
}Response fields
| Field | Type | Description |
|---|---|---|
data.prompt.id | uuid | required. Use it with Get prompt |
data.prompt.prompt_text | string | required |
data.prompt.topic_id | uuid | null | required |
data.prompt.funnel_stages | string[] | required. Empty until the classifier runs |
data.prompt.is_active | boolean | required. Always true |
data.country_codes | string[] | required. The markets actually used, after defaulting |
data.quota.limit | integer | null | required. Your plan’s active-prompt limit, null if unlimited |
data.quota.used | integer | required. Including the prompt you just created |
data.quota.remaining | integer | required. What is left after this call |
request_id | string | required. Also returned as the x-request-id header |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | Unknown field, missing or over-long text, a bad country code, or a topic_id / tag_ids not on this brand |
| 401 | invalid_token | The key is missing, malformed, or does not exist |
| 403 | insufficient_scope | The key does not carry write:prompts |
| 403 | plan_required | This workspace is not enabled for API writes |
| 403 | quota_exceeded | The request would exceed your active-prompt allowance. Nothing is created |
| 404 | not_found | The brand does not exist, or this key is not allowed to reach it |
| 409 | conflict | This brand already tracks a prompt with that text |
| 429 | rate_limited | Over 60 requests per minute |
| 503 | service_unavailable | Your allowance could not be checked. Nothing is created. Retry shortly |
quota_exceeded and plan_required are both 403 and mean different things. plan_required means
your plan does not include API writes at all, and the fix is an upgrade. quota_exceeded means it
does and you have used your slots, where deleting prompts you no longer track may be enough.
Notes
Quota is per market, not per prompt. A prompt targeting five countries takes five slots. The check runs before anything is written and covers the whole request, so a prompt is never half-created in two of the three markets you asked for.
If the allowance cannot be checked, nothing is created. A 503 here means the check itself was
unavailable and we refused rather than guessing. Retry; it is transient.
The prompt exists immediately; its data does not. Funnel stage, search-demand tier and the first
results come from background work. Reading it back straight away returns empty funnel_stages and
null metrics. That is expected, not a failure.
Duplicates are rejected. Text that already exists for the brand, ignoring case and whitespace,
returns 409 conflict rather than creating a second copy.
Prompts created here are marked api. The provenance is distinct from prompts added by hand in
the app, so a surprising bill can be traced to the integration that caused it.
Idempotency-Key is required, and matters more here than elsewhere. Reuse it when retrying and
the original response is replayed rather than a second prompt being created and a second slot spent.
See Idempotency.