# createReport

mutation · in the family [Report](/reference/report/)

## What it does

Save a report definition — a plan with columns, or a group-by and measures; validated now, run later as whoever runs it.

Create a Report in the caller's org group; requires the unrestricted capability.

## What happens

Creates a record; nothing runs until someone runs it.

## Who may call it

Capability area: **Work and notes** — Tasks, notes, messages, dashboards and the everyday tools every staff member keeps.

- Owner
- System Administrator
- Manager
- Associate Manager
- Warehouse Associate
- Sales Associate
- Cashier
- An API key whose scope allows `api:createReport`

## Arguments

| Name | Type | Required | Notes |
| --- | --- | --- | --- |
| `input` | [NewReportInput](/types/NewReportInput/) `NewReportInput!` | yes | No further notes. |

## Returns

[Report](/types/Report/) `Report!` — A Report (📊; THE REPORT ENGINE, program 3 of THE OFFICE FIX PROGRAM 2): a SAVED REPORT DEFINITION — a validated ReportSpec (the reportRun grammar VERBATIM: a plan + columns, or a plan + groupBy/measures, an optional sort) kept as JSON text (specJson, at most 12288 characters) with its words (describeReport, at most 4096 characters) and the family its rows or groups are made of (terminalFamily). It stores NO result: every run executes as THE RUNNER under the runner's own list rights through reportRun(reportId) (rows on screen, now) or startExportJob(reportId, format) (a file, in the background); the saved spec is RE-VALIDATED at every run (a roster that moved since the save refuses in the teaching voice — edit the spec); only an active definition runs. The definition is the question, never an answer. NOT searchable (no filter roster until the report builder lands).

## Example request

```graphql
mutation ExampleCreateReport($input: NewReportInput!) {
  createReport(input: $input) {
    id
    sysId
    type
    caption
    status
    parentId
    rootId
    createdAt
    updatedAt
    revisionNum
    revision
    specJson
    words
    terminalFamily
    description
  }
}
```

Variables:

```json
{
  "input": {
    "caption": "Blue jeans",
    "spec": {
      "plan": {
        "start": {
          "family": "<family>",
          "filter": {
            "clauses": [
              {
                "field": "<field>",
                "op": "<op>",
                "values": [
                  "<values>"
                ]
              }
            ]
          }
        },
        "hops": [
          {
            "direction": "outbound",
            "field": "<field>",
            "family": "<family>",
            "filter": {
              "clauses": [
                {
                  "field": "<field>",
                  "op": "<op>",
                  "values": [
                    "<values>"
                  ]
                }
              ]
            }
          }
        ]
      },
      "columns": [
        "<columns>"
      ]
    }
  }
}
```

Send it with the envelope naming the version: `"extensions": {"at": {"version": {"name":"genesis","number":0}}}`.

## Example response

```json
{
  "data": {
    "createReport": {
      "id": "01900000-0000-7000-8000-37386ae00000",
      "sysId": "RO-EXMP-0000-000F",
      "type": "Report",
      "caption": "Blue jeans",
      "status": "active",
      "parentId": "01900000-0000-7000-8000-065235280000",
      "rootId": "01900000-0000-7000-8000-a093dd800000",
      "createdAt": "2027-01-31T00:00:00.000Z",
      "updatedAt": "2027-01-31T00:00:00.000Z",
      "revisionNum": 1,
      "revision": "01900000-0000-7000-8000-b7960e180000",
      "specJson": "<spec json>",
      "words": "<words>",
      "terminalFamily": "<terminal family>",
      "description": "Straight-cut, mid-rise, five pockets."
    }
  },
  "extensions": {
    "at": {
      "callId": "01EXAMPLE-CALL-ID",
      "version": {
        "requested": {
          "name": "genesis",
          "number": 0
        },
        "serviced": {
          "name": "genesis",
          "number": 0
        }
      }
    }
  }
}
```

## Errors this call can answer

- `VALIDATION/INVALID` — Something in the request is not valid. ([VALIDATION](/errors/VALIDATION/))
- `AUTHN/REQUIRED` — Sign in to do this. ([AUTHN](/errors/AUTHN/))
- `AUTHZ/FORBIDDEN` — Your role does not allow this action. ([AUTHZ](/errors/AUTHZ/))
- `RATE_LIMIT/THROTTLED` — Too many requests in a short time. ([RATE_LIMIT](/errors/RATE_LIMIT/))
- `CONFLICT/*` — The record’s state, or a change made in the meantime, does not allow this; the codes are on the CONFLICT page. ([CONFLICT](/errors/CONFLICT/))
- `VALIDATION/VERSION_REQUIRED` — The request did not say which app version it came from. ([VALIDATION](/errors/VALIDATION/))

## Dry run

Add `dryRun: true` to the request envelope (`extensions.at`) to rehearse this call: every check runs, the write is rehearsed against the current records and nothing is stored; the answer is the refusal a real call would give, or the record it would create. Every response to a rehearsal carries `dryRun: true`, so a rehearsed record is never mistaken for a saved one.
