Skip to main content
A key with a write scope can change a project the way Prompts and Project settings do in the app: Creating projects is not in the API; do that in the app. The MCP server has no write tools at all: writes are REST only.

A key writes as the person who made it

The write scopes are off when a key is made, and only someone who ticks them gets them. A write then acts as the person who made the key, with their role:
  • A key never does more than its maker could do in the app. An owner, admin or editor can make these changes; a viewer cannot.
  • The maker is checked again on every write. If they have left the workspace or become a viewer, the key’s writes are refused with 403 forbidden (“the person who made this key can no longer change this workspace, so the key cannot either”). Its reads carry on. Make a new key as someone who can edit.
  • A key with no recorded maker cannot write at all; make a new one.
  • Turning on an engine that is an opt-in is a spending decision, so it needs an owner or admin, as in the app.
  • Citino records every write against both the person and the key, so a change can always be traced to the key that made it.
Each write is one transaction: the check and the change happen together, or not at all. Send writes from a server or a script. A write that carries an Origin header naming another site, as a request from a browser page does, is refused with 403 forbidden.

Prompts

Add a prompt

string
required
The question, exactly as a buyer would ask it, up to 300 characters. Prompt text never changes once added, so check it before you send it.
string
required
What the buyer is after: discover, use-case, compare, alternative, pricing, trust or support.
string[]
required
The segment ids the prompt belongs to. Use segments the project has claimed: a prompt only counts towards your scores in a claimed segment. An empty list is accepted, and leaves the prompt out of every score. The project’s segments, claimed or not, are on GET /v1/projects/{projectId}.
The answer is 201 with the new prompt_id, its cadence, and first_run_date: null: the prompt is collected from the next scheduled day, exactly like one added in the app. scope says how Citino classified the question: unbranded questions (the ones behind your headline figures and reports) read "category", a question that names your brand or one competitor reads "branded", and one that names two or more tracked brands reads "versus". The refusals worth handling:
  • The same text again is 409 conflict with detail.existing_prompt_id (and detail.first_run_date). Identity is the exact text, so a retried script converges on that id. The match includes prompts you have archived or re-worded, which are not collected, so check that prompt’s status on the prompts list before treating it as live. Normalise your text (trim it, settle on one spelling) before you send it, because a different capital letter is a different prompt.
  • A segment the project has not claimed is 409 conflict; detail.unclaimed and detail.claimed list both.
  • At the plan’s prompt limit is 402 plan_limit, with detail.used, detail.limit and detail.actions naming the two ways out, upgrade and archive. The limit is checked before the duplicate, so at the limit even the same text again is 402, not 409.
  • An archived project takes no new prompts: 409 conflict.

Re-word a prompt

Because prompt text never changes, a re-wording is a new prompt that takes the old one’s place. Send text, a wording the project does not already track (archived and replaced prompts count, and so does the prompt’s own wording); one it does track is 409 conflict with detail.existing_prompt_id, as when adding. Otherwise the answer is 201 with the new prompt_id and replaces_id, the prompt it replaced. The old prompt stops being collected, keeps its history, and is marked as replaced by the new one. Compare the two over time by their ids.

Archive a prompt

POST …/prompts/{promptId}/archive with no body stops collecting a prompt and answers {"archived": true}. Its history stays. A prompt that is not active (already archived, or replaced) is 404.

Competitors

Track a competitor

Send brand_id: the id of any brand a report or the league table has returned. Competitors are tracked by id only; to track a brand Citino has never seen named, add it by name in the app.
  • 201 with the brand’s brand_id, name and domain when it was added, and 200 with the same body when it was already tracked, so a retry is safe.
  • An unknown brand id is 404; your own brand is 400, with field set to brand_id; at the plan’s competitor limit, 402 plan_limit with detail.limit and detail.used.

Stop tracking a competitor

DELETE …/competitors/{brandId} answers 204. Nothing measured is deleted. A brand that is not tracked is 404.

Aliases

An alias is another name answers use for your brand: a short form, an old name, a common misspelling.
  • POST …/aliases with {"alias": "…"} answers 201 with alias_id, brand_id and rescan, a sentence saying the answers already collected are being re-scanned for the new name (or, when the re-scan could not be queued just then, that the alias is saved and how to start the re-scan). The re-scan reads stored answers only: no engine is asked anything and no prompt allowance is used.
  • An alias that already belongs to another brand is 409 conflict, and detail.owning_brand_id and detail.owning_brand_name name that brand. An alias is 3 to 128 characters; outside that is 400 validation.
  • DELETE …/aliases/{aliasId} answers 204. Only an alias your workspace added that has not been reviewed yet can be removed (source is customer and reviewed is false); any other id is 404. Your project’s aliases and their ids are on GET /v1/projects/{projectId}; read them before adding, so a re-run does not add twice.

Segments and engines

Both are PUTs that replace the whole list, so sending the same body twice leaves the same result.
  • PUT …/segments with {"segments": [ids]} sets the segments the project claims: at least one, taken from the project’s segments on GET /v1/projects/{projectId}. An id the project does not have is 400 validation with field set to segments and the id in the message, and nothing changes; an id sent twice is claimed once. Your scores are recomputed over the history already stored on the next read; nothing new is collected. The answer has claimed and recomputed, a sentence saying so.
  • PUT …/engines with {"engines": [keys]} sets the engines the project samples from now on. Turning one off removes nothing already measured. At least one must be an engine that is being collected (400 otherwise); an opt-in engine your plan does not carry is 402. The answer has engines, as stored, and history, a sentence saying what happened to past answers. GET /v1/engines lists every engine and how a project gets it.

Retrying safely

Authentication

Making a key with a write scope.

Errors

conflict, plan_limit and forbidden.

Project settings

The same changes, in the app.

Plans and limits

Prompt and competitor limits.