> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowpayroll.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Save a report definition



## OpenAPI

````yaml /api-reference/specs/report.json post /report-definitions
openapi: 3.0.1
info:
  title: Report Service
  description: Customisable reporting API for FlowPayroll.
  contact:
    name: FlowPayroll
    email: hello@flowpayroll.com
  version: v1
servers:
  - url: https://api.sandbox.flowpayroll.ai/v1/report
security:
  - X-Auth-Token: []
    X-Org-Id: []
tags:
  - name: Datasets
    description: >-
      Discover what you can query: list the available datasets and inspect each
      field catalog — types, filterability, groupability, and supported
      aggregations.
  - name: Run reports
    description: >-
      Run a report synchronously — either an inline report definition or a saved
      one — and get columns and rows back in the response.
  - name: Report definitions
    description: >-
      Save a report definition under a name so it can be re-run, exported, and
      scheduled. Create, list, retrieve, replace, and delete definitions.
  - name: Exports
    description: >-
      Queue large result sets as asynchronous exports (CSV or JSONL, up to
      5,000,000 rows) and poll for a presigned download URL.
  - name: Schedules
    description: >-
      Run a saved definition on a cron schedule and deliver the export by
      webhook or email.
  - name: Run history
    description: Audit trail of every report run — synchronous, export, and scheduled.
paths:
  /report-definitions:
    post:
      tags:
        - Report definitions
      summary: Save a report definition
      operationId: createReportDefinition
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveDefinitionRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DefinitionResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '409':
          $ref: '#/components/responses/Conflict'
components:
  schemas:
    SaveDefinitionRequest:
      type: object
      required:
        - name
        - definition
      properties:
        name:
          type: string
          description: Unique per organisation
        definition:
          $ref: '#/components/schemas/ReportDefinition'
        description:
          type: string
          nullable: true
    DefinitionResponse:
      type: object
      required:
        - definitionId
        - name
        - definition
        - createdAt
        - updatedAt
      properties:
        definitionId:
          type: string
        name:
          type: string
        description:
          type: string
          nullable: true
        definition:
          $ref: '#/components/schemas/ReportDefinition'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    ReportDefinition:
      type: object
      required:
        - dataset
        - select
      description: >-
        The report-definition document — one contract drives sync runs, exports,
        saved definitions and schedules. See docs/API.md for the full rules.
      properties:
        dataset:
          type: string
          description: A dataset name from GET /datasets
        select:
          type: array
          minItems: 1
          maxItems: 50
          items:
            $ref: '#/components/schemas/SelectEntry'
        filters:
          $ref: '#/components/schemas/FilterNode'
        groupBy:
          type: array
          items:
            type: string
          nullable: true
          description: >-
            Required iff any select entry aggregates; every plain select entry
            must appear here, referenced by field name or alias
        orderBy:
          type: array
          items:
            $ref: '#/components/schemas/OrderBy'
          nullable: true
        limit:
          type: integer
          format: int32
          nullable: true
          description: Clamped to 10,000 (sync) / 5,000,000 (export)
    ValidationErrorResponse:
      type: object
      required:
        - error
        - errors
      properties:
        error:
          type: string
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ValidationErrorDetail'
    ServiceError:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
        message:
          type: string
    SelectEntry:
      type: object
      description: >-
        A plain field ({field}), an aggregation ({aggregate, field?, as}), or a
        tag-value extraction ({field, tagName, tagGroup?, as}) on a tags field.
        count needs no field; count with a field supports distinct.
      properties:
        field:
          type: string
          nullable: true
        aggregate:
          type: string
          enum:
            - sum
            - avg
            - min
            - max
            - count
          nullable: true
        as:
          type: string
          nullable: true
          description: >-
            Output column alias (required for aggregations and tag-value
            extractions)
        distinct:
          type: boolean
          default: false
        tagName:
          type: string
          nullable: true
          description: >-
            Extract this tag's value from a tags field as a text column;
            groupable via its alias
        tagGroup:
          type: string
          nullable: true
          description: >-
            Restrict tagName extraction to one tag group; omitted matches any
            group
    FilterNode:
      type: object
      description: >-
        Filter tree node: exactly one of and/or/not (branch, depth ≤ 3) or
        field+op (leaf). value is type-checked against the field.
      properties:
        and:
          type: array
          items:
            $ref: '#/components/schemas/FilterNode'
          nullable: true
        or:
          type: array
          items:
            $ref: '#/components/schemas/FilterNode'
          nullable: true
        not:
          allOf:
            - $ref: '#/components/schemas/FilterNode'
          nullable: true
        field:
          type: string
          nullable: true
        op:
          type: string
          enum:
            - eq
            - neq
            - in
            - gt
            - gte
            - lt
            - lte
            - between
            - contains
            - isNull
            - inPeriod
          nullable: true
        value:
          nullable: true
          description: >-
            Scalar, or array for in/between; omitted for isNull; a
            relative-period token (@currentWeek, @currentMonth, @currentTaxYear)
            for inPeriod on a date field
    OrderBy:
      type: object
      required:
        - field
      properties:
        field:
          type: string
          description: A selected field or aggregation alias
        direction:
          type: string
          enum:
            - asc
            - desc
          default: asc
    ValidationErrorDetail:
      type: object
      required:
        - path
        - message
      properties:
        path:
          type: string
          description: JSON pointer into the offending request
        message:
          type: string
  responses:
    ValidationError:
      description: Invalid report definition or request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorResponse'
    Unauthorised:
      description: No organisation context on the request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ServiceError'
    Conflict:
      description: Conflict
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ServiceError'
  securitySchemes:
    X-Auth-Token:
      type: apiKey
      description: FlowPayroll JWT (raw token, no Bearer prefix)
      name: X-Auth-Token
      in: header
    X-Org-Id:
      type: apiKey
      description: >-
        Organisation to scope the request to. Required when the principal can
        access more than one organisation; optional for single-organisation
        principals (the authorizer resolves it automatically).
      name: X-Org-Id
      in: header

````