Manage Report Access

Update a report's title, description, config, and data sources, and share it with EDITOR/VIEWER collaborators.


Use the updateReport mutation to change a report’s title, description, or config, replace its data sources, and manage who it’s shared with. Sharing lives inside the same mutation: the reportUsers field on UpdateReportInput is the report’s collaborator list, where each entry grants a user the EDITOR or VIEWER role.

Reports are scoped to one organization (Company in the API) and start private to their creator. Workspaces are Project objects and records are Todo objects.

updateReport only updates the fields you supply — but the list-valued fields (dataSources, reportUsers) are wholesale replacements, not merges. See Replacement semantics before you call it.

Request

The smallest useful call updates a single scalar. Omitted fields are left untouched.

mutation RenameReport {
  updateReport(id: "report_123", input: { title: "Q3 Pipeline (revised)" }) {
    id
    title
    updatedAt
  }
}

To share a report, pass reportUsers. This call shares the report with two collaborators — one editor, one viewer:

mutation ShareReport {
  updateReport(
    id: "report_123"
    input: {
      reportUsers: [{ userId: "user_123", role: EDITOR }, { userId: "user_456", role: VIEWER }]
    }
  ) {
    id
    reportUsers {
      role
      user {
        id
        fullName
        email
      }
    }
  }
}

Parameters

The id argument is the report’s ID. The caller must be the report’s creator or an EDITOR — see Permissions.

UpdateReportInput

ParameterTypeRequiredDescription
titleStringNoNew display name. Only applied if supplied.
descriptionStringNoNew free-text description. Maximum 10,000 characters.
configJSONObjectNoApplication-defined view configuration (opaque key/value blob). Maximum 100 KB once JSON-serialized.
dataSources[UpdateReportDataSourceInput!]NoIf supplied, replaces every existing data source (delete-then-recreate). Omit to leave them unchanged. At most 50 entries. See Replacement semantics.
reportUsers[EditReportUserInput!]NoIf supplied, the full reconciled collaborator list — adds new users, updates roles, and removes anyone not present. Omit to leave sharing unchanged.

UpdateReportDataSourceInput

ParameterTypeRequiredDescription
idStringNoAccepted for symmetry with the read type but ignored — every source is recreated with a new identifier. See the note below.
nameStringNoOptional label for the data source.
sourceTypeReportSourceTypeNoWhat the data source aggregates. Defaults to TODOS (the only value today).
projectIds[String!]NoWorkspace IDs (or slugs) to scope this source to. null or an empty array means all workspaces the creator can access; a non-empty array means those specific workspaces.
filtersJSONObjectNoFilter applied to records in this source — an opaque blob mirroring the Records filter inputs.
orderIntNoSort position of the data source within the report. Defaults to its index in the supplied list.

EditReportUserInput

ParameterTypeRequiredDescription
userIdString!YesThe user to grant access to. Must be a member of the report’s organization.
roleReportRole!YesThe access level to grant — EDITOR or VIEWER.

ReportRole

ValueGrants
EDITORView the report and call updateReport (rename, reconfigure, re-share, replace data sources). Cannot delete the report.
VIEWERView the report, read its data and aggregations, duplicate it, and refresh aggregations. Cannot modify it.

Replacement semantics

The two list fields are reconciled wholesale, not patched. Plan your input accordingly.

  • dataSources is a full replace. Supplying it deletes all existing data sources and recreates the report’s data sources from the array you pass. There is no way to patch a single source through this path: even though UpdateReportDataSourceInput carries an id, the resolver ignores it and assigns every recreated source a fresh identifier. To “edit one source,” resend the complete set with your change applied. Omit dataSources entirely to leave the existing sources alone.
  • reportUsers is a full reconciled share list. Supplying it makes the array the complete set of collaborators: users in the array are added or have their role updated, and any current collaborator not in the array is removed. Send an empty array [] to unshare the report from everyone. Omit reportUsers to leave sharing unchanged.
dataSources replaces, never merges

If you send dataSources to change one source, include every source you want to keep. Sending a single-element array drops all the others. The same applies to reportUsers — omit the field to preserve the current share list; sending a partial array removes everyone you left out.

The projectIds convention is identical to Create a Report: null or [] means all workspaces the report creator can access (workspace access is always resolved against the creator, not the editor making the change), and a non-empty array means those specific workspaces. The computed Report.projectIds field returns the union across all sources.

Response

updateReport returns the updated Report!. The example below corresponds to the ShareReport call above.

{
  "data": {
    "updateReport": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "reportUsers": [
        {
          "role": "EDITOR",
          "user": {
            "id": "clm4n8r1a000108l0a2bcde90",
            "fullName": "Dana Editor",
            "email": "[email protected]"
          }
        },
        {
          "role": "VIEWER",
          "user": {
            "id": "clm4n8r1a000208l0f5ghij12",
            "fullName": "Vic Viewer",
            "email": "[email protected]"
          }
        }
      ]
    }
  }
}

Report

updateReport returns the full Report. The complete Report and ReportDataSource field reference lives on Query Reports; the fields relevant to this page are below.

FieldTypeDescription
idID!The report’s unique identifier.
titleString!The report’s display name.
descriptionStringThe description, or null.
configJSONObjectApplication-defined view configuration, or null.
dataSources[ReportDataSource!]!The report’s data sources after the update, in order.
reportUsers[ReportUser!]!The current collaborator list. See ReportUser.
updatedAtDateTime!Bumped to now on a successful update.

ReportUser

Each entry in Report.reportUsers is a ReportUser — one collaborator the report is shared with.

FieldTypeDescription
idID!The share record’s unique identifier.
uidString!Public unique identifier for the share record.
userUser!The collaborator. Select id, fullName, email.
roleReportRole!The granted access level — EDITOR or VIEWER.
createdAtDateTime!When the report was first shared with this user.
updatedAtDateTime!When this share record was last changed (e.g. role).

Full example

Replace the report’s data sources and update its config in one call. This drops any previously configured sources and rebuilds them from the array, while leaving the share list untouched (because reportUsers is omitted):

mutation ReconfigureReport {
  updateReport(
    id: "report_123"
    input: {
      title: "Open deals — EMEA & US"
      config: { layout: "table", groupBy: "stage" }
      dataSources: [
        {
          name: "EMEA"
          sourceType: TODOS
          projectIds: ["project_123"]
          filters: { done: false }
          order: 0
        }
        {
          name: "US"
          sourceType: TODOS
          projectIds: ["project_456"]
          filters: { done: false }
          order: 1
        }
      ]
    }
  ) {
    id
    title
    config
    dataSources {
      id
      name
      projectIds
      order
    }
  }
}

Supplying dataSources triggers a transactional backfill of list items for the referenced workspaces, exactly as on create. If the backfill fails for any workspace, the entire update rolls back with Failed to backfill N of M projects. Update aborted. Failed projects: … — no partial change is applied. Sources scoped to all workspaces (projectIds: null) reference no specific IDs and never trigger this.

Errors

CodeWhen
UNAUTHENTICATEDNo authenticated user on the request.
FORBIDDENThe request has no active company context.
REPORT_NOT_FOUNDNo report with that id is visible to the caller as creator or EDITOR — also returned when the report exists but the caller only has VIEWER access (no editable report is found).
USER_NOT_IN_COMPANYA reportUsers entry references a userId that is not a member of the report’s organization.
BAD_USER_INPUTMalformed input (e.g. a JSONObject field that isn’t a valid object).

The data-source count (max 50), description length (max 10,000 chars), and config size (max 100 KB serialized) limits, plus the backfill rollback, throw a plain error with no machine-readable extension code — the message states the cause (e.g. Config size (… bytes) exceeds maximum of 102400 bytes).

Permissions

Report access has four tiers. updateReport sits in the modify tier:

TierWhoOperations
ViewCreator or any collaborator (EDITOR or VIEWER)report, refreshReportAggregations, duplicateReport
ModifyCreator or collaborators with role EDITORupdateReport (this page)
DeleteCreator onlydeleteReport
ExportCreator, any collaborator, or any member of the report’s organizationexportReport — intentionally the broadest tier

A VIEWER cannot call updateReport — the lookup returns REPORT_NOT_FOUND rather than a distinct authorization error, because the resolver finds no report the caller is permitted to modify. To let someone reconfigure or re-share a report, grant them EDITOR. Only the creator can delete a report; transfer of ownership is not available through this API.