Duplicate & Delete a Report

Clone a report and its data sources, or hard-delete a report and its data sources, with the duplicateReport and deleteReport mutations.


Use the duplicateReport mutation to clone a report, and the deleteReport mutation to remove one. Duplicating copies the report’s title, description, config, and every data source — but not its shared collaborators; the duplicate starts private to you, and you become its creator. Deleting hard-deletes the report and cascade-deletes its data sources, and is restricted to the report’s creator.

A report aggregates records (Todo objects) across one or more workspaces (Project objects); see the Reports overview for the full model.

Request

Duplicate a report. The input argument is optional — omit it to accept the default title.

mutation DuplicateReport {
  duplicateReport(id: "report_123") {
    id
    title
    createdAt
  }
}

Pass a title to name the copy explicitly.

mutation DuplicateReportWithTitle {
  duplicateReport(id: "report_123", input: { title: "Q4 Pipeline (working copy)" }) {
    id
    title
  }
}

Delete a report. deleteReport returns Boolean!, so it takes no sub-selection.

mutation DeleteReport {
  deleteReport(id: "report_123")
}

Parameters

duplicateReport

ParameterTypeRequiredDescription
idString!YesID of the report to duplicate. You must have view access (creator, EDITOR, or VIEWER).
inputDuplicateReportInputNoOptions for the copy. Omit entirely to use defaults.

DuplicateReportInput

ParameterTypeRequiredDescription
titleStringNoTitle for the copy. If omitted, the copy is titled "Copy of <original title>".

deleteReport

ParameterTypeRequiredDescription
idString!YesID of the report to delete. You must be the report’s creator (see below).

Response

duplicateReport returns the newly created Report.

{
  "data": {
    "duplicateReport": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "title": "Copy of Q4 Pipeline",
      "createdAt": "2026-05-29T14:03:11.000Z"
    }
  }
}

deleteReport returns true on success.

{
  "data": {
    "deleteReport": true
  }
}

Returns — duplicateReport

The mutation returns the new Report. The duplicate lives in the same organization as the original, and you are its creator.

FieldTypeDescription
idID!Unique identifier of the new report.
uidString!Stable UID for the report.
titleString!Title of the copy.
descriptionStringDescription copied from the original.
configJSONObjectOpaque view configuration copied from the original (see note).
createdByUser!The user who ran the duplication. The caller always becomes the creator.
dataSources[ReportDataSource!]!Data sources copied from the original, each recreated with a fresh uid.
reportUsers[ReportUser!]!Empty — collaborators are not copied. The duplicate starts private to you.
lastGeneratedAtDateTimenull on a fresh duplicate until aggregations are first computed.
createdAtDateTime!When the copy was created.
updatedAtDateTime!When the copy was last modified.

For the full Report and ReportDataSource field reference (including the computed projectIds union and the todos / todoCount / aggregations query surface), see Query Reports and Report Data & Aggregations.

Returns — deleteReport

TypeDescription
Boolean!true when the report and its data sources are deleted. Never false.

What gets copied

duplicateReport deep-copies the report into a new record in the same organization:

  • The report — a new Report with a fresh id and uid. The title (defaulting to "Copy of <title>"), description, and config are copied verbatim.
  • All data sources — every ReportDataSource is recreated with a fresh uid, preserving its name, sourceType, projectIds, filters, and order. A data source with projectIds: null (all workspaces the creator can access) stays null on the copy.
  • Creator — the calling user becomes createdBy, regardless of who created the original.

It deliberately does not copy:

  • Shared collaboratorsreportUsers is not carried over. The duplicate starts private to you; re-share it with updateReport if needed.
Data sources reference the new creator's workspaces

Because each data source resolves projectIds: null against the report creator’s workspace access, a copied data source on the duplicate resolves against your access, not the original creator’s. If you have access to fewer workspaces than the original creator, the duplicate’s record set can differ from the original’s.

deleteReport is a permanent, cascading delete

deleteReport hard-deletes the report — there is no soft-delete, archive, or undo. Its data sources are cascade-deleted with it. The underlying records (Todo objects) and workspaces are untouched; only the report and its configuration are removed.

Errors

CodeWhen
UNAUTHENTICATEDNo valid credentials on the request.
REPORT_NOT_FOUNDNo report matches id for the caller. For duplicateReport this also covers no view access; for deleteReport it also covers any caller who is not the creator (an EDITOR or VIEWER simply finds no matching report).
{
  "errors": [
    {
      "message": "Report was not found.",
      "extensions": { "code": "REPORT_NOT_FOUND" }
    }
  ]
}

Omitting the required id is rejected by GraphQL as a non-null validation error before the resolver runs — it is not a Blue error code.

Permissions

The two mutations sit at opposite ends of the report permission model.

MutationRequired access
duplicateReportView access — creator, or a shared reportUser with role EDITOR or VIEWER.
deleteReportCreator only. Shared EDITOR and VIEWER collaborators cannot delete.

Anyone who can see a report can duplicate it (the duplicate is theirs, sharing nothing back to the original). Deleting is the most restricted report operation — even an EDITOR who can update the report cannot delete it. A caller who lacks the required access receives REPORT_NOT_FOUND rather than a distinct permission error.

The config field is a JSONObject — an opaque key/value blob whose internal shape is defined by the app, not the GraphQL schema. duplicateReport copies it as-is.