Skip to main content

Project Tags

Tags are account-level labels — a name and a colour — that group the Analyze projects, Protect test suites and AI Visibility projects in an account. One pool of tags covers all three: a project can carry any number of tags, and a tag can be applied to any number of projects.

Creating, changing, deleting, reordering and applying tags after project creation require the admin role on the account. Reading them requires no special role.

Listing an account's tags

Operation: query GetAccountTags($accountId: ObjectID!, $after: String) { getAccount(id: $accountId) { id tags(first: 100, after: $after) { edges { node { id name color } } pageInfo { hasNextPage endCursor } } } }Variables: { "accountId": "TjAwN0FjY291bnQ3MTU", "after": null }Response Example: { "data": { "getAccount": { "id": "TjAwN0FjY291bnQ3MTU", "tags": { "edges": [ { "node": { "id": "TjAwOFRhZzE", "name": "Production", "color": "#4CAF50" } }, { "node": { "id": "TjAwOFRhZzI", "name": "Staging", "color": "#FF9800" } } ], "pageInfo": { "hasNextPage": false, "endCursor": "Mg" } } } } }
GetAccountTagsTry in Explorer
GraphQL
query GetAccountTags($accountId: ObjectID!, $after: String) {
getAccount(id: $accountId) {
id
tags(first: 100, after: $after) {
edges {
node {
id
name
color
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}

Tags come back in the order an admin arranged them (see Display order) unless you pass your own orderBy.

Creating a tag

Operation: mutation CreateAccountTag($input: CreateAccountTagInput!) { createAccountTag(input: $input) { accountTag { id name color position } } }Variables: { "input": { "accountId": "TjAwN0FjY291bnQ3MTU", "name": "Production", "color": "#4CAF50" } }
CreateAccountTagTry in Explorer
GraphQL
mutation CreateAccountTag($input: CreateAccountTagInput!) {
createAccountTag(input: $input) {
accountTag {
id
name
color
position
}
}
}

color must be a hex colour. A new tag is appended after the account's existing tags, so its position is one past the current highest.

Use updateAccountTag to rename or recolour a tag, and deleteAccountTag to remove one. Deleting a tag is allowed only when it has no project or user assignments. Otherwise ACCOUNT_TAG_IN_USE returns projects, aiVisibilityProjects and users reference counts in its error extensions. Remove those assignments first.

Applying tags to existing projects

createAccountTagProjects links tags to projects, and deleteAccountTagProjects unlinks them. Both take lists, so one call can apply several tags to several projects. Every tag and every project in the call must belong to the same account; mixing accounts is rejected with ACCOUNT_TAG_PROJECT_ACCOUNT_MISMATCH, which is also what supplying a foreign tag to a create mutation returns.

Applying tags when creating a project

Every project- and test-suite-creating mutation accepts accountTagIds, which saves a second round trip:

Operation: mutation CreateTaggedProject($input: CreateProjectInput!) { createSEOProject(input: $input) { project { id name tags { id name } } } }Variables: { "input": { "accountId": "TjAwN0FjY291bnQ3MTU", "name": "example.com", "primaryDomain": "https://www.example.com/", "accountTagIds": ["TjAwOFRhZzE", "TjAwOFRhZzI"] } }
CreateTaggedProjectTry in Explorer
GraphQL
mutation CreateTaggedProject($input: CreateProjectInput!) {
createSEOProject(input: $input) {
project {
id
name
tags {
id
name
}
}
}
}

The same input field is available on createBasicProject, createAccessibilityProject, createSiteSpeedProject, createTestSuite, createAccessibilityTestSuite and createSiteSpeedTestSuite.

Creating a project needs the editor role. A tag-restricted editor may supply only tags assigned to them in accountTagIds, or omit tags to create a project visible to everyone. Admins may choose any tag in the account. Other editors must omit tags. After creation, adding or removing project tags remains admin-only. This creation exception also applies to AI Visibility projects and Protect test suites. When a restricted user clones a project, only source tags assigned to that user are copied.

Protect test suites

Test suites are tagged from the same pool of account tags as projects. TestSuite.tags returns a test suite's tags, and Account.testSuites accepts the same tagIds filter as Account.projects:

Operation: query GetTaggedTestSuites($accountId: ObjectID!, $tagIds: [ObjectID!]) { getAccount(id: $accountId) { testSuites(first: 20, tagIds: $tagIds) { edges { node { id name tags { id name color } } } totalCount } } }Variables: { "accountId": "TjAwN0FjY291bnQ3MTU", "tagIds": ["TjAwOFRhZzE"] }
GetTaggedTestSuitesTry in Explorer
GraphQL
query GetTaggedTestSuites($accountId: ObjectID!, $tagIds: [ObjectID!]) {
getAccount(id: $accountId) {
testSuites(first: 20, tagIds: $tagIds) {
edges {
node {
id
name
tags {
id
name
color
}
}
}
totalCount
}
}
}

AI Visibility projects

AI Visibility projects are tagged from the same pool of account tags, through their own pair of mutations: createAccountTagAiVisibilityProjects and deleteAccountTagAiVisibilityProjects. Both take an explicit accountId alongside the lists of tags and projects, because AI Visibility project ids are only unique within the account that owns them:

Operation: mutation TagAiVisibilityProjects($input: CreateAccountTagAiVisibilityProjectsInput!) { createAccountTagAiVisibilityProjects(input: $input) { aiVisibilityProjects { id name } accountTags { id name } } }Variables: { "input": { "accountId": "TjAwN0FjY291bnQ3MTU", "accountTagIds": ["TjAwOFRhZzE"], "aiVisibilityProjectIds": ["TjAwOEFpVmlzaWJpbGl0eVByb2plY3Qx"] } }
TagAiVisibilityProjectsTry in Explorer
GraphQL
mutation TagAiVisibilityProjects($input: CreateAccountTagAiVisibilityProjectsInput!) {
createAccountTagAiVisibilityProjects(input: $input) {
aiVisibilityProjects {
id
name
}
accountTags {
id
name
}
}
}

AiVisibilityProject.tags returns a project's tags, and getAiVisibilityProjects accepts a tagIds filter:

Operation: query GetTaggedAiVisibilityProjects($accountId: ObjectID!, $tagIds: [ObjectID!]) { getAiVisibilityProjects(accountId: $accountId, first: 20, tagIds: $tagIds) { nodes { id name tags { id name color } } totalCount } }Variables: { "accountId": "TjAwN0FjY291bnQ3MTU", "tagIds": ["TjAwOFRhZzE"] }
GetTaggedAiVisibilityProjectsTry in Explorer
GraphQL
query GetTaggedAiVisibilityProjects($accountId: ObjectID!, $tagIds: [ObjectID!]) {
getAiVisibilityProjects(accountId: $accountId, first: 20, tagIds: $tagIds) {
nodes {
id
name
tags {
id
name
color
}
}
totalCount
}
}

createAiVisibilityProject accepts accountTagIds in the same way the project-creating mutations do, with the same creation-only exception for a restricted editor’s assigned tags.

Filtering by tag

Pass tagIds to Account.projects, Account.testSuites or getAiVisibilityProjects to return only the projects carrying at least one of those tags:

Operation: query GetTaggedProjects($accountId: ObjectID!, $tagIds: [ObjectID!]) { getAccount(id: $accountId) { projects(first: 20, tagIds: $tagIds) { edges { node { id name tags { id name color } } } totalCount } } }Variables: { "accountId": "TjAwN0FjY291bnQ3MTU", "tagIds": ["TjAwOFRhZzE"] }
GetTaggedProjectsTry in Explorer
GraphQL
query GetTaggedProjects($accountId: ObjectID!, $tagIds: [ObjectID!]) {
getAccount(id: $accountId) {
projects(first: 20, tagIds: $tagIds) {
edges {
node {
id
name
tags {
id
name
color
}
}
}
totalCount
}
}
}

Display order

Each tag carries a position that decides the order tags are listed in within its account. It is not a priority or a grouping — it only controls display.

Treat the value itself as opaque: sort by it, don't compute with it. Positions ascend in display order, but they are not guaranteed to be consecutive or to start at zero, and a given tag's number can change when other tags are added, removed or reordered. Only the relative order is meaningful.

reorderAccountTags sets the whole account's order in one call. Pass every tag the account has, exactly once, in the order you want:

Operation: mutation ReorderAccountTags($input: ReorderAccountTagsInput!) { reorderAccountTags(input: $input) { accountTags { id name position } } }Variables: { "input": { "accountId": "TjAwN0FjY291bnQ3MTU", "accountTagIds": ["TjAwOFRhZzI", "TjAwOFRhZzE"] } }
ReorderAccountTagsTry in Explorer
GraphQL
mutation ReorderAccountTags($input: ReorderAccountTagsInput!) {
reorderAccountTags(input: $input) {
accountTags {
id
name
position
}
}
}

Listing a subset, repeating a tag, or including a tag from another account is rejected with ACCOUNT_TAG_ORDER_INCOMPLETE and changes nothing, so a partial list cannot silently reshuffle the tags it omits. The error's extensions name the offending ids under missingIds, unknownIds and duplicateIds. Read the account's tags first if you need the current set.

Restricting a member's project visibility

An account admin can replace an existing member's visibility tags with updateAccountUserTags:

Operation: mutation UpdateAccountUserTags($input: UpdateAccountUserTagsInput!) { updateAccountUserTags(input: $input) { account { id } } }Variables: { "input": { "accountId": "12345", "userId": "67890", "accountTagIds": ["123"] } }
UpdateAccountUserTagsTry in Explorer
GraphQL
mutation UpdateAccountUserTags($input: UpdateAccountUserTagsInput!) {
updateAccountUserTags(input: $input) { account { id } }
}

AccountUser.tags returns the member's current assignments. Every supplied tag must belong to the same account; the mutation rejects foreign tags with ACCOUNT_TAG_PROJECT_ACCOUNT_MISMATCH. The replacement accepts up to 1,000 tag IDs and takes effect on subsequent requests.

A non-admin member with one or more tags sees only projects carrying at least one of those tags, plus every untagged project. This applies to Analyze, Protect and AI Visibility, including project data in dashboards, searches, direct-ID lookups, downloads and connection counts. An additional tagIds filter narrows these results; it never grants access to other projects. A hidden direct-ID lookup behaves like a missing project.

No assigned tags means access to every project in that account. Before clearing the last tag, your application should warn that the member will gain access to all projects. Tags are independent between accounts and do not change a member's role or the operations that role allows.

Admins always see all projects and cannot receive visibility tags (ACCOUNT_TAG_ADMIN_USER). Promoting a member to admin clears their tags. A subsequent demotion starts with unrestricted visibility until an admin assigns tags again. Removing and re-adding a membership also starts without visibility tags.

Authenticated users can create personal API keys, including Viewers. These keys use their owner's current membership, role and tags. Service accounts inherit their creator's current visibility tags and continue working if their creator is no longer an admin. While the creator is tag-restricted, the service account cannot perform account-admin operations, including changing member roles or visibility tags. If the creator is removed from the account, the service account remains enabled but loses access to all projects, including untagged projects. Legacy service accounts without a recorded creator retain their existing account scope. Creating service accounts and their keys requires account admin access. Sharelinks remain explicit access grants.

Project email notifications check tagged members' current project access when notifications are sent. Configured external recipients and account integrations remain explicit delivery grants.

Monitor dashboards and boards

A dashboard is visible only when every project behind every view is visible to the caller. One hidden project hides the whole dashboard. Accessible dashboards render all their views with the same counts for everyone who can open them. A board appears when at least one of its dashboards is visible. Creating a board without dashboard inputs initializes a default Monitor dashboard for a tag-restricted member so the new board remains reachable. Deleting a board requires visibility of every dashboard it contains; a mixed board cannot be deleted by someone who cannot see all of its dashboards. Bulk sorting preserves the position slots occupied by hidden boards and dashboards. Moving a single board requires visibility of every board in the account; otherwise it returns ACCESS_DENIED_PERMISSIONS_MISSING without changing the order. Visibility does not add a partial editing state: existing role permissions continue to apply to the whole accessible dashboard or alert.

Linking projects with different tag sets is allowed. Linked datasource content renders in full for anyone who can open the project, including someone who cannot see the project the data came from. This is a known and accepted gap in the Tag boundary. Linked data is never hidden at read time.

Before creating a link, call previewProjectLinkTags with accountId, projectId and aiVisibilityProjectId. Before a tag edit, supply either project ID and the proposed complete projectTagIds or aiVisibilityProjectTagIds set. Omit a tag set to read its current value; an empty array previews removing every tag. With one project ID, the API checks every existing link affected by that edit. The response names both tag sets and explains that anyone who can see the AI Visibility project can read linked crawl data whatever tags they hold. Show these warnings and allow the user to continue. This preview does not save changes or block linking. A saved link exposes the same information through ProjectAiVisibilitySettings.tagWarningsForLink. Warning messages and the empty-tag label follow the request locale, including Japanese; tag names remain unchanged.

AI Visibility usage

Account-wide credits, prompt limits, provider entitlements and run totals remain visible in full. getAiVisibilityRunUsage.projectUsage names only visible projects. It combines hidden projects into one Other projects row with no project ID; its estimatedRuns values sum to accountEstimatedRuns. These are planning-window estimates (including the modeled project's draft settings), not actual credit consumption. Actual account credit usage remains available in currentCreditUsage.

Crawl-project settings do not expose a linked AI Visibility project that the caller cannot see. Link-warning previews omit inaccessible AI Visibility links. The accepted linked-data exception applies only from an accessible AI Visibility project to its crawl datasource.

A warning preview without either project ID returns PROJECT_LINK_PREVIEW_PROJECT_REQUIRED. A crawl project outside the selected account returns PROJECT_LINK_PREVIEW_ACCOUNT_MISMATCH. Both errors are localized.