Skip to main content

Projects

A project is the top-level container for AI Visibility monitoring. Each project has a primary brand, a set of AI providers, and a schedule that controls automatic prompt execution.

List projects​

Retrieve all AI Visibility projects for an account. Results include metadata like topic and prompt counts, configured AI providers, and the primary brand domain.

Operation: query GetAiVisibilityProjects($accountId: ObjectID!) { getAiVisibilityProjects( accountId: $accountId first: 10 ) { nodes { id rawId name totalTopics totalPrompts aiProviderTypes primaryBrandDomain primaryBrandRawId scheduleCadence nextScheduleRunAt autoCrawlEnabled freshnessThresholdDays country language createdAt updatedAt } pageInfo { hasNextPage endCursor } totalCount } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ" }
GetAiVisibilityProjectsTry in Explorer
GraphQL
query GetAiVisibilityProjects($accountId: ObjectID!) {
getAiVisibilityProjects(
accountId: $accountId
first: 10
) {
nodes {
id
rawId
name
totalTopics
totalPrompts
aiProviderTypes
primaryBrandDomain
primaryBrandRawId
scheduleCadence
nextScheduleRunAt
autoCrawlEnabled
freshnessThresholdDays
country
language
createdAt
updatedAt
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}

You can search projects by name using the search parameter:

Operation: query SearchAiVisibilityProjects($accountId: ObjectID!, $search: String!) { getAiVisibilityProjects( accountId: $accountId first: 10 search: $search ) { nodes { id name primaryBrandDomain } totalCount } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ", "search": "my brand" }
SearchAiVisibilityProjectsTry in Explorer
GraphQL
query SearchAiVisibilityProjects($accountId: ObjectID!, $search: String!) {
getAiVisibilityProjects(
accountId: $accountId
first: 10
search: $search
) {
nodes {
id
name
primaryBrandDomain
}
totalCount
}
}

Sorting​

Projects can be sorted by: name, primaryBrandDomain, totalTopics, totalPrompts.

Get a single project​

Fetch a specific project by its ID:

Operation: query GetAiVisibilityProject( $accountId: ObjectID! $aiVisibilityProjectId: ObjectID! ) { getAiVisibilityProject( accountId: $accountId aiVisibilityProjectId: $aiVisibilityProjectId ) { id rawId name scheduleCadence nextScheduleRunAt autoCrawlEnabled freshnessThresholdDays primaryBrand { id rawId name } aiProviders { id name type } createdAt updatedAt } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ", "aiVisibilityProjectId": "QWlWaXNpYmlsaXR5UHJvamVjdDE" }
GetAiVisibilityProjectTry in Explorer
GraphQL
query GetAiVisibilityProject(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
) {
getAiVisibilityProject(
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
) {
id
rawId
name
scheduleCadence
nextScheduleRunAt
autoCrawlEnabled
freshnessThresholdDays
primaryBrand {
id
rawId
name
}
aiProviders {
id
name
type
}
createdAt
updatedAt
}
}

Create a project​

Creating a project requires a name, brand name, and brand domain. The primary brand is automatically created as an own type brand.

Operation: mutation CreateAiVisibilityProject( $accountId: ObjectID! $name: String! $brandName: String! $brandDomain: String! $scheduleCadence: AiVisibilityScheduleCadence $brandThemeSentimentCaptureEnabled: Boolean! $country: String $language: String ) { createAiVisibilityProject( input: { accountId: $accountId name: $name brandName: $brandName brandDomain: $brandDomain scheduleCadence: $scheduleCadence brandThemeSentimentCaptureEnabled: $brandThemeSentimentCaptureEnabled country: $country language: $language } ) { aiVisibilityProject { id rawId name scheduleCadence nextScheduleRunAt brandThemeSentimentCaptureEnabled country language createdAt } } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ", "name": "My Brand Monitoring (UK)", "brandName": "Lumar", "brandDomain": "lumar.io", "scheduleCadence": "Daily", "brandThemeSentimentCaptureEnabled": false, "country": "GB", "language": "en" }
CreateAiVisibilityProjectTry in Explorer
GraphQL
mutation CreateAiVisibilityProject(
$accountId: ObjectID!
$name: String!
$brandName: String!
$brandDomain: String!
$scheduleCadence: AiVisibilityScheduleCadence
$brandThemeSentimentCaptureEnabled: Boolean!
$country: String
$language: String
) {
createAiVisibilityProject(
input: {
accountId: $accountId
name: $name
brandName: $brandName
brandDomain: $brandDomain
scheduleCadence: $scheduleCadence
brandThemeSentimentCaptureEnabled: $brandThemeSentimentCaptureEnabled
country: $country
language: $language
}
) {
aiVisibilityProject {
id
rawId
name
scheduleCadence
nextScheduleRunAt
brandThemeSentimentCaptureEnabled
country
language
createdAt
}
}
}

Project limit​

Your subscription caps how many projects an account may hold. Account.aiVisibilityProjectsLimit is that cap and Account.aiVisibilityProjectsCount is the number of projects already created, so gate a "create project" action on those two fields rather than re-deriving the limit: createAiVisibilityProject enforces exactly these two numbers and fails with AI_VISIBILITY_PROJECTS_LIMIT_REACHED once they meet. Deleted projects are not counted.

aiVisibilityProjectsLimit is null when the account has no project cap, and the mutation then allows creation without limit, so test for it before comparing:

aiVisibilityProjectsLimit == null || aiVisibilityProjectsCount < aiVisibilityProjectsLimit

Comparing a null limit numerically would disable creation on exactly the accounts that have no cap.

Schedule cadence​

The scheduleCadence field controls how often prompts are automatically sent to AI providers:

ValueDescription
DailyEvery day (default)
WeeklyOnce per week
EveryTwoWeeksEvery two weeks
MonthlyOnce per month
EveryTwoMonthsEvery two months
QuarterlyOnce every three months

The schedule cadence also affects the maximum number of prompts allowed in a project. More frequent cadences allow fewer prompts per project.

AI providers​

By default, all AI providers included in your subscription are linked to the project. You can optionally specify aiVisibilityAiProviderIds to select a subset of providers.

Market: country and language​

A project can carry a market: country (ISO 3166-1 alpha-2, e.g. GB) and language (ISO 639-1, e.g. en). One project targets one market; to compare the same prompts across the US and the UK, create two projects.

Both fields are optional and three-state on createAiVisibilityProject:

InputStored value
Field omittedCopied from the account's defaultCountry / defaultLanguage, if set
Explicit nullnull (worldwide / unspecified), even when the account has a default
A value ("gb")Validated and normalised (GB, en)

The value is copied at creation time. Changing an account default later never rewrites existing projects. See Account settings.

When language is omitted and the account has no defaultLanguage, the project does not stay language-less: the language is detected from the brand domain you create the project with, and stored. Detection is best-effort and time-boxed, so a site that cannot be read, or that establishes no clear language, leaves language as null. It also runs only for requests authenticated as a user: a service-account API key creates the project without detection, so pass language explicitly on those calls. An explicit null is never detected over -- it keeps its meaning of "unspecified on purpose". Read language back after creating a project rather than assuming the value you sent.

What the fields do:

  • country is copied onto every prompt created without an explicit country (both createAiVisibilityPrompt and createAiVisibilityTopicsBulk). An explicit prompt country, including null for worldwide, always wins, so accounts that set country per prompt are unaffected. Prompt runs keep using the prompt's stored country; the existing country read filter is unchanged.
  • language steers generateAiVisibilitySuggestedTopics, generateAiVisibilitySuggestedPrompts and generateAiVisibilityTopicMetadata, which also use country for local context (competitors, spelling, regulations). Prompts themselves carry no language.

Generation never changes a project's language: it uses what the project carries. A project with no language -- one created before detection existed, or one whose detection found nothing -- generates as it always did, so set language on those projects yourself if the output language matters.

Unknown or malformed codes are rejected as input validation errors. Country is case-insensitive and stored upper-case; language must be lower-case.

To build a country or language picker, list the accepted codes with their English names. Neither query requires authentication:

Operation: query GetAiVisibilityMarketCodes { aiVisibilityCountries { code name } aiVisibilityLanguages { code name } }
GetAiVisibilityMarketCodesTry in Explorer
GraphQL
query GetAiVisibilityMarketCodes {
aiVisibilityCountries {
code
name
}
aiVisibilityLanguages {
code
name
}
}

Country codes apply to account defaults, projects, prompts, and the free scan. Language codes apply to account defaults, projects, and the free scan; prompts carry no language.

Update a project​

Update a project's name, schedule cadence, market, or content evaluation settings:

Operation: mutation UpdateAiVisibilityProject( $accountId: ObjectID! $aiVisibilityProjectId: ObjectID! $name: String $scheduleCadence: AiVisibilityScheduleCadence $autoCrawlEnabled: Boolean $freshnessThresholdDays: Int $brandThemeSentimentCaptureEnabled: Boolean $country: String $language: String ) { updateAiVisibilityProject( input: { accountId: $accountId aiVisibilityProjectId: $aiVisibilityProjectId name: $name scheduleCadence: $scheduleCadence autoCrawlEnabled: $autoCrawlEnabled freshnessThresholdDays: $freshnessThresholdDays brandThemeSentimentCaptureEnabled: $brandThemeSentimentCaptureEnabled country: $country language: $language } ) { aiVisibilityProject { id name scheduleCadence autoCrawlEnabled freshnessThresholdDays brandThemeSentimentCaptureEnabled country language } } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ", "aiVisibilityProjectId": "QWlWaXNpYmlsaXR5UHJvamVjdDE", "name": "Updated Project Name", "scheduleCadence": "Weekly", "autoCrawlEnabled": true, "freshnessThresholdDays": 14, "brandThemeSentimentCaptureEnabled": true }
UpdateAiVisibilityProjectTry in Explorer
GraphQL
mutation UpdateAiVisibilityProject(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
$name: String
$scheduleCadence: AiVisibilityScheduleCadence
$autoCrawlEnabled: Boolean
$freshnessThresholdDays: Int
$brandThemeSentimentCaptureEnabled: Boolean
$country: String
$language: String
) {
updateAiVisibilityProject(
input: {
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
name: $name
scheduleCadence: $scheduleCadence
autoCrawlEnabled: $autoCrawlEnabled
freshnessThresholdDays: $freshnessThresholdDays
brandThemeSentimentCaptureEnabled: $brandThemeSentimentCaptureEnabled
country: $country
language: $language
}
) {
aiVisibilityProject {
id
name
scheduleCadence
autoCrawlEnabled
freshnessThresholdDays
brandThemeSentimentCaptureEnabled
country
language
}
}
}

Omitted fields are left unchanged. For country and language, pass null to clear the value. Changing the market only affects prompts created afterwards and future generation; existing prompts and their runs are not rewritten, so retargeting a market is normally a new project.

Content evaluation settings​

  • autoCrawlEnabled -- When true, cited pages from your brand's domains are automatically crawled and evaluated after each prompt run
  • freshnessThresholdDays -- Number of days (1-90, default 7) before a previously crawled URL is considered stale and re-crawled

Theme sentiment capture​

  • brandThemeSentimentCaptureEnabled -- When true, the project requests per-theme brand sentiment capture on its prompt runs. Off by default. This is a request, not a guarantee: capture is performed by the prompt tracker, which may not honour the request, so theme sentiment data can remain empty even while the setting is enabled. An empty sentiment series is therefore a normal state, not an error. The setting can also be passed on createAiVisibilityProject, so a project can opt in before its first prompt runs.
caution

Changing scheduleCadence may reduce the allowed prompt count. If the project's active prompt count exceeds the new per-project limit, the update will fail with AI_VISIBILITY_PROMPTS_LIMIT_REACHED. A project sitting exactly at the new limit can still change cadence, and the account-wide limit is not checked here because cadence does not affect it -- see Create a prompt.

Check run capacity before adding prompts​

getAiVisibilityRunUsage models how a project's configuration consumes the account's AI Visibility capacity, so a client can tell how many prompts can still be added before saving. Pass a draft promptCount, scheduleCadence or aiProviderTypes to model a change that is not saved yet; each defaults to the project's current value, and previewAiProviderType models enabling one more provider.

Operation: query GetAiVisibilityRunUsage( $accountId: ObjectID! $aiVisibilityProjectId: ObjectID! $promptCount: Int $scheduleCadence: AiVisibilityScheduleCadence ) { getAiVisibilityRunUsage( accountId: $accountId aiVisibilityProjectId: $aiVisibilityProjectId promptCount: $promptCount scheduleCadence: $scheduleCadence ) { promptCount effectivePromptsLimit accountPromptsLimit accountPromptsRoom roomToAddPrompts bindingConstraint bindingPoolKey overLimit pools { key type totalRuns availableRuns overLimit } } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ", "aiVisibilityProjectId": "QWlWaXNpYmlsaXR5UHJvamVjdDE", "promptCount": 60 }
GetAiVisibilityRunUsageTry in Explorer
GraphQL
query GetAiVisibilityRunUsage(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
$promptCount: Int
$scheduleCadence: AiVisibilityScheduleCadence
) {
getAiVisibilityRunUsage(
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
promptCount: $promptCount
scheduleCadence: $scheduleCadence
) {
promptCount
effectivePromptsLimit
accountPromptsLimit
accountPromptsRoom
roomToAddPrompts
bindingConstraint
bindingPoolKey
overLimit
pools {
key
type
totalRuns
availableRuns
overLimit
}
}
}

The response weighs three constraints and reports the tightest one:

  • Credit pools (pools) -- the provider-specific and shared credit buckets the selected providers draw on, with demand normalised to the fixed planningPeriodDays window. bindingPoolKey names the pool when it is the binding constraint.

    Normalising can leave a project owing a fraction of a run: at cadenceFactor 1/3, a quarterly prompt costs a third of a run over a 30-day window. Demand is summed across every project and provider drawing on a pool before being rounded up, so three quarterly prompts cost one run rather than three. Rounding once per pool means usedRuns, projectedRuns, availableRuns, overageRuns and each entry in segments are whole runs that add up to the pool total, while overLimit and roomToAddPrompts follow the unrounded demand. A configuration with fractional demand -- any cadence other than daily or monthly -- can therefore report slightly lower usage than the sum of its parts suggests.

  • Per-project prompt limit (effectivePromptsLimit) -- the project's own cap, from the subscription, provider count and schedule cadence.

  • Account-wide prompt limit (accountPromptsLimit, accountPromptsRoom) -- the budget shared by every project on the account and the part of it this project can still use (the limit minus active prompts on the account's other projects). Both are null when the account has no account-wide limit. See Create a prompt for how the two prompt limits are enforced.

roomToAddPrompts is the number of prompts the modelled configuration can still add under the binding constraint, and bindingConstraint says which one binds: Pool, ProjectPromptLimit or AccountPromptLimit. overLimit is true when the modelled prompt count already exceeds any of them, and previewCanBeEnabled answers whether a previewAiProviderType would still fit.

Tags​

Projects can be grouped with the account's tags — the same pool that covers Analyze projects and Protect test suites. AiVisibilityProject.tags returns a project's tags, getAiVisibilityProjects takes a tagIds filter, and createAiVisibilityProject takes accountTagIds. Applying tags requires the account's admin role, while creating a project only requires editor, so an editor passing accountTagIds creates nothing.

See Project Tags for the tag lifecycle and the linking mutations.

Delete a project​

Soft-deletes a project. This is a destructive operation.

Operation: mutation DeleteAiVisibilityProject( $accountId: ObjectID! $aiVisibilityProjectId: ObjectID! ) { deleteAiVisibilityProject( input: { accountId: $accountId aiVisibilityProjectId: $aiVisibilityProjectId } ) { aiVisibilityProject { id } } }Variables: { "accountId": "TjAwN0FjY291bnQxMjM0NQ", "aiVisibilityProjectId": "QWlWaXNpYmlsaXR5UHJvamVjdDE" }
DeleteAiVisibilityProjectTry in Explorer
GraphQL
mutation DeleteAiVisibilityProject(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
) {
deleteAiVisibilityProject(
input: {
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
}
) {
aiVisibilityProject {
id
}
}
}

Schema reference​