Project & Workspace Subscriptions

Real-time streams for workspace lifecycle, membership, folders, tags, and saved views over the GraphQL WebSocket.


Subscribe to changes in your workspaces and their organizing structure. Workspaces are Project objects in the API, so every operation on this page is named subscribeToProject*, subscribeToFolder, subscribeToTag, subscribeToSavedView, or an imperative on* event. They share the GraphQL WebSocket transport (wss://api.blue.cc/graphql) and the same authenticated handshake as every other subscription — see Connect & Authenticate for the connectionParams setup.

Two payload shapes appear here:

  • Change-feed subscriptions (subscribeToProject, subscribeToFolder, subscribeToTag, subscribeToSavedView, onSetProjectFolder) return a *SubscriptionPayload with the shared shape { mutation, node, previousValues, updatedFields }. The mutation field is a MutationType (CREATED, UPDATED, or DELETED). This shape is explained once on the Real-time overview.
  • Bare-entity events (onAddedToProject, onRemovedFromProject, onArchiveProject, onUnarchiveProject, onConvertToTemplate, onRemovedFromTemplate) resolve directly to a Project! — no envelope. onCopyProjectStarted / onCopyProjectFinished resolve to a CopyProjectStatus.
Events are gated per delivery

Subscribing always succeeds. You only receive events for workspaces you can see: each event is filtered server-side against your project membership before it is delivered. Pass company and project IDs as either the ID or the slug.

subscribeToProject

The primary workspace change-feed. Streams CREATED / UPDATED / DELETED events for every workspace you belong to within a company. Use the optional arguments to narrow the stream to a folder, to archived workspaces, or to templates.

Request

subscription WatchProjects {
  subscribeToProject(companyId: "company_123") {
    mutation
    node {
      id
      name
      archived
      isTemplate
      folder {
        id
        title
      }
    }
    updatedFields
    previousValues {
      name
      archived
    }
  }
}

Parameters

ArgumentTypeRequiredDescription
companyIdString!YesOrganization to watch, by ID or slug. Only workspaces in this company are delivered.
folderIdStringNoRestrict to workspaces filed in this folder for the current user. Omit to watch every folder; pass null explicitly to watch only un-foldered workspaces.
archivedBooleanNoWhen true, deliver only archived workspaces.
isTemplateBooleanNoWhen true, deliver only templates. With neither archived nor isTemplate set, only active (non-archived, non-template) workspaces are delivered.

Response

Each delivered event is a ProjectSubscriptionPayload.

{
  "data": {
    "subscribeToProject": {
      "mutation": "UPDATED",
      "node": {
        "id": "clm4n8qwx000008l0g4oxdqn7",
        "name": "Q3 Launch Plan",
        "archived": false,
        "isTemplate": false,
        "folder": { "id": "folder_123", "title": "Marketing" }
      },
      "updatedFields": ["name"],
      "previousValues": { "name": "Q3 Plan", "archived": false }
    }
  }
}

ProjectSubscriptionPayload

FieldTypeDescription
mutationMutationType!CREATED, UPDATED, or DELETED.
nodeProjectThe workspace’s current state. null on DELETED.
updatedFields[String!]Names of the workspace fields that changed on UPDATED.
previousValuesProjectPreviousValuesPrior values for the changed fields. Exposes the scalar subset: id, uid, slug, name, description, archived, isTemplate, isOfficialTemplate, category, createdAt, updatedAt.

subscribeToFolder

Streams folder CREATED / UPDATED / DELETED events. Folders come in two kinds, selected by the required type argument: workspace folders (PROJECT, shared org-wide) and file folders (FILE, scoped to a workspace).

Request

subscription WatchProjectFolders {
  subscribeToFolder(companyId: "company_123", type: PROJECT) {
    mutation
    node {
      id
      title
      type
      parent {
        id
        title
      }
    }
    updatedFields
  }
}

Parameters

ArgumentTypeRequiredDescription
companyIdString!YesOrganization to watch, by ID or slug.
typeFolderType!YesPROJECT (workspace folders, delivered to any company member) or FILE (file folders inside a workspace).
projectIdStringNoRequired for FILE folders — the workspace whose file folders you want. Ignored for PROJECT.
parentIdStringNoRestrict to children of this folder. Pass null to watch only top-level folders; omit to watch every level.

FolderType

ValueDescription
PROJECTFolders that organize workspaces. Shared across the whole organization.
FILEFolders that organize files inside a single workspace.

Response

{
  "data": {
    "subscribeToFolder": {
      "mutation": "CREATED",
      "node": {
        "id": "folder_123",
        "title": "Marketing",
        "type": "PROJECT",
        "parent": null
      },
      "updatedFields": null
    }
  }
}

FolderSubscriptionPayload carries { mutation, node: Folder, updatedFields, previousValues: FolderPreviousValues }.

subscribeToTag

Streams tag CREATED / UPDATED / DELETED events for a single workspace. Tags carry a title and color (there is no Tag.name).

Request

subscription WatchTags {
  subscribeToTag(projectId: "project_123") {
    mutation
    node {
      id
      title
      color
    }
    updatedFields
  }
}

Parameters

ArgumentTypeRequiredDescription
projectIdString!YesWorkspace to watch, by ID or slug.

Response

{
  "data": {
    "subscribeToTag": {
      "mutation": "UPDATED",
      "node": { "id": "tag_123", "title": "Urgent", "color": "#FF4D4D" },
      "updatedFields": ["color"]
    }
  }
}

TagSubscriptionPayload carries { mutation, node: Tag, updatedFields, previousValues: TagPreviousValues }.

subscribeToSavedView

Streams saved-view CREATED / UPDATED / DELETED events for a workspace. Saved views are SavedView objects. Shared views (isShared: true) are delivered to every workspace member; personal views are delivered only to their creator.

Request

subscription WatchSavedViews {
  subscribeToSavedView(projectId: "project_123") {
    mutation
    node {
      id
      name
      viewType
      isShared
    }
  }
}

Parameters

ArgumentTypeRequiredDescription
projectIdString!YesWorkspace to watch, by ID or slug.

Response

{
  "data": {
    "subscribeToSavedView": {
      "mutation": "CREATED",
      "node": {
        "id": "clm4n8qwx000008l0g4oxdqn7",
        "name": "My open records",
        "viewType": "DATABASE",
        "isShared": false
      }
    }
  }
}

SavedViewSubscriptionPayload carries { mutation, node: SavedView, previousValues: SavedView } — note there is no updatedFields on this payload, and previousValues is a full SavedView rather than a scalar subset.

See Saved Views for the matching read and write operations.

subscribeToProjectPeopleList

subscription WatchProjectPeople {
  subscribeToProjectPeopleList(projectId: "project_123") {
    mutation
    node {
      id
      fullName
      email
    }
  }
}
ArgumentTypeRequiredDescription
projectIdString!YesWorkspace whose membership list you want.

Returns a UserSubscriptionPayload ({ mutation, node: User, updatedFields, previousValues: UserPreviousValues }).

No publisher wired

This subscription accepts a connection but its resolver currently always resolves null — no membership events are emitted. To react to membership changes today, use onAddedToProject / onRemovedFromProject below, which are fully wired. Treat subscribeToProjectPeopleList as reserved.

subscribeToProjectUserRole

subscription WatchMyProjectRole {
  subscribeToProjectUserRole(input: { projectId: "project_123", userId: "user_123" }) {
    mutation
    node {
      id
      name
    }
  }
}

SubscribeToProjectUserRoleInput

FieldTypeRequiredDescription
projectIdString!YesWorkspace to watch.
userIdString!YesThe member whose role assignment you want to track.

Returns a ProjectUserRoleSubscriptionPayload ({ mutation, node: ProjectUserRole, previousValues: ProjectUserRole }). Like the people-list stream, this channel is a stub with no production publisher — prefer the wired on* events for membership changes.

Imperative project events (on*)

These channels fire on specific lifecycle actions rather than streaming a generic change feed. Each is delivered only to members of the affected workspace (and, where a companyId argument exists, only for that organization).

onAddedToProject

Fires for the current user when they are added to a workspace. Resolves to the Project! they joined. Takes no arguments — it is implicitly scoped to the authenticated user.

subscription OnJoinedProject {
  onAddedToProject {
    id
    name
    company {
      id
      slug
    }
  }
}
{
  "data": {
    "onAddedToProject": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "name": "Q3 Launch Plan",
      "company": { "id": "company_123", "slug": "acme" }
    }
  }
}

onRemovedFromProject

Mirror of onAddedToProject: fires for the current user when they are removed from a workspace, resolving to the Project! they left. No arguments.

subscription OnLeftProject {
  onRemovedFromProject {
    id
    name
  }
}

onArchiveProject

Fires when a workspace in the given organization is archived. Resolves to the archived Project!.

subscription OnArchive {
  onArchiveProject(companyId: "company_123") {
    id
    name
    archived
  }
}
ArgumentTypeRequiredDescription
companyIdString!YesOrganization to watch, by ID or slug.

onUnarchiveProject

Inverse of onArchiveProject — fires when a workspace is restored from the archive. Same companyId argument, resolves to the restored Project!.

subscription OnUnarchive {
  onUnarchiveProject(companyId: "company_123") {
    id
    name
    archived
  }
}

onSetProjectFolder

Fires when a workspace is moved into (or out of) a folder. Unlike the other project on* events, this one returns the full change-feed envelope, ProjectSubscriptionPayload!, so you can read the workspace’s new state from node.

subscription OnFolderChange {
  onSetProjectFolder(companyId: "company_123", folderId: "folder_123") {
    mutation
    node {
      id
      name
      folder {
        id
        title
      }
    }
  }
}
ArgumentTypeRequiredDescription
companyIdString!YesOrganization to watch (matched by ID).
folderIdStringNoDeliver only when the workspace is filed into this folder. Pass null to watch moves out of all folders (to the top level).

onConvertToTemplate

Fires when a workspace in the organization is converted into a template. Resolves to the affected Project! (now isTemplate: true).

subscription OnConvertToTemplate {
  onConvertToTemplate(companyId: "company_123") {
    id
    name
    isTemplate
  }
}
ArgumentTypeRequiredDescription
companyIdString!YesOrganization to watch, by ID or slug.

onRemovedFromTemplate

Inverse of onConvertToTemplate — fires when a workspace is converted back from a template into a regular workspace. Resolves to the affected Project!.

subscription OnRemovedFromTemplate {
  onRemovedFromTemplate(companyId: "company_123") {
    id
    name
    isTemplate
  }
}
ArgumentTypeRequiredDescription
companyIdString!YesOrganization to watch, by ID or slug.

onCopyProjectStarted / onCopyProjectFinished

Copying a workspace (or a large template) is a background job. These two channels report its lifecycle to the user who started the copy: onCopyProjectStarted fires when the job is queued, onCopyProjectFinished when it completes. Both resolve to a CopyProjectStatus and take no arguments — each is implicitly scoped to the authenticated user who triggered the copy.

subscription OnCopyProgress {
  onCopyProjectStarted {
    newProjectName
    isTemplate
    isActive
    queuePosition
    totalQueues
    newCompany {
      id
      slug
    }
  }
}
{
  "data": {
    "onCopyProjectStarted": {
      "newProjectName": "Q3 Launch Plan (copy)",
      "isTemplate": false,
      "isActive": true,
      "queuePosition": 2,
      "totalQueues": 3,
      "newCompany": { "id": "company_123", "slug": "acme" }
    }
  }
}

CopyProjectStatus

FieldTypeDescription
oldCompanyCompanySource organization.
oldProjectProjectSource workspace being copied.
newCompanyCompanyDestination organization.
newProjectNameStringName of the copy being created.
isTemplateBooleanWhether the copy is a template.
isActiveBooleanWhether the copy job is currently running.
queuePositionIntPosition in the copy queue while waiting.
totalQueuesIntTotal jobs in the queue.

Pair these with the copyProject mutation in Workspaces to drive a progress UI.

Errors

Subscription errors surface either at the WebSocket handshake (connection-level) or on the individual subscription operation.

CodeWhen
UNAUTHENTICATEDThe connectionParams carry no valid credential. The handshake is rejected before any subscription runs — see Connect & Authenticate.
BAD_USER_INPUTA required argument is missing or malformed (e.g. companyId omitted on subscribeToProject, or type omitted on subscribeToFolder).
FORBIDDENThe authenticated user lacks access to the scoped organization or workspace.

Permissions

You may open any of these subscriptions, but delivery is filtered per event:

  • Workspace-scoped streams (subscribeToProject, subscribeToTag, subscribeToSavedView, the project on* events) deliver only for workspaces where you are a member. subscribeToSavedView additionally hides personal views you did not create.
  • subscribeToFolder with type: PROJECT delivers to any member of the organization, because workspace folders are shared org-wide. With type: FILE it is scoped to members of the named workspace.
  • onCopyProjectStarted / onCopyProjectFinished deliver only to the user who initiated the copy.