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
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
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:
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:
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:
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:
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:
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:
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:
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.
Links between AI Visibility and crawl projects
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.