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.
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}.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 conflictwithdetail.existing_prompt_id(anddetail.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’sstatuson 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.unclaimedanddetail.claimedlist both. - At the plan’s prompt limit is
402 plan_limit, withdetail.used,detail.limitanddetail.actionsnaming the two ways out,upgradeandarchive. The limit is checked before the duplicate, so at the limit even the same text again is402, not409. - 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. Sendtext, 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
Sendbrand_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.
201with the brand’sbrand_id,nameanddomainwhen it was added, and200with the same body when it was already tracked, so a retry is safe.- An unknown brand id is
404; your own brand is400, withfieldset tobrand_id; at the plan’s competitor limit,402 plan_limitwithdetail.limitanddetail.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 …/aliaseswith{"alias": "…"}answers201withalias_id,brand_idandrescan, 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, anddetail.owning_brand_idanddetail.owning_brand_namename that brand. An alias is 3 to 128 characters; outside that is400 validation. DELETE …/aliases/{aliasId}answers204. Only an alias your workspace added that has not been reviewed yet can be removed (sourceiscustomerandreviewedisfalse); any other id is404. Your project’s aliases and their ids are onGET /v1/projects/{projectId}; read them before adding, so a re-run does not add twice.
Segments and engines
Both arePUTs that replace the whole list, so sending the same body twice leaves the same result.
PUT …/segmentswith{"segments": [ids]}sets the segments the project claims: at least one, taken from the project’s segments onGET /v1/projects/{projectId}. An id the project does not have is400 validationwithfieldset tosegmentsand 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 hasclaimedandrecomputed, a sentence saying so.PUT …/engineswith{"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 (400otherwise); an opt-in engine your plan does not carry is402. The answer hasengines, as stored, andhistory, a sentence saying what happened to past answers.GET /v1/engineslists every engine and how a project gets it.
Retrying safely
Related
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.