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

# Create custom widget

> Creates a custom widget for the caller's organization. It is listed in the `custom` category of Read widgets, and a card that uses it gets its own copy. The server sets `isCustom: true` and `category: custom`.



## OpenAPI

````yaml /openapi/public/openapi-analytics.json post /dynamic-dashboard-widget
openapi: 3.0.0
info:
  title: API - Analytics
  version: 3.0.0
  description: API Documentation - RESTful API for field operations management.
servers:
  - url: https://apiweb.mile.app/api/v3
security:
  - bearerAuth: []
tags:
  - name: Dashboard
    description: >-
      Dashboard lets a user compose their own dashboard from any number of
      cards, start from a template, duplicate a dashboard, share it with roles,
      and save it back as an organization template. Each card owns its own
      widget row (a chart type plus a query spec), cloned from the widget
      catalogue (built-in widgets plus the organization's custom widgets), so
      renaming or editing a card never touches the catalogue or another
      dashboard. Dashboards are owned by their creator: roles they are shared
      with can view, duplicate and save as template, but only the owner can edit
      or delete. Every write reuses the dashboard permission family (view, add,
      edit and delete dashboard).


      The numbers behind each card come from one generic aggregation endpoint
      instead of one endpoint per chart: the client sends a batch of up to 12
      declarative query specs, the server injects the caller's organization, hub
      and soft-delete scope from the session, runs each spec read-only, and
      returns a flat columns and rows table per item. The catalog endpoints
      describe the data sources, fields and limits a client may use.
  - name: Report
    description: >-
      A report is a saved table definition over tasks of one flow or records of
      one data type: which fields to show, which hubs to include, and the
      default filters.
  - name: Insight Report
    description: >-
      Insight reports are written summaries of your operations, generated from
      your task and data records: findings, a summary and suggested actions. You
      set the context once per flow, then generate reports on demand or on a
      schedule.
paths:
  /dynamic-dashboard-widget:
    post:
      tags:
        - Dashboard
      summary: Create custom widget
      description: >-
        Creates a custom widget for the caller's organization. It is listed in
        the `custom` category of Read widgets, and a card that uses it gets its
        own copy. The server sets `isCustom: true` and `category: custom`.
      operationId: createDynamicDashboardWidget
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - viz
                - spec
              properties:
                name:
                  type: string
                  maxLength: 200
                  description: Literal display name, never translated
                description:
                  type: string
                viz:
                  type: string
                  maxLength: 50
                  description: >-
                    Chart type: one of the chart types used by the built-in
                    query widgets (currently `bar`, `donut`, `heatmap`, `kpi`,
                    `line`, `table`)
                spec:
                  type: object
                  description: >-
                    Query spec, validated the same way as Run analytics query. A
                    `timeRange` in it is refused with `400 dydsh-014`; the
                    dashboard supplies it.
                  additionalProperties: true
                defaultLayout:
                  type: object
                  description: Default card size when the widget is added to a dashboard
                  properties:
                    w:
                      type: integer
                      minimum: 2
                      maximum: 48
                      description: Width in grid columns, 2-48
                    h:
                      type: integer
                      minimum: 3
                      maximum: 18
                      description: Height in rows, 3-18; `null` = as tall as its content
              example:
                name: Done tasks per hub
                description: Completed tasks per hub in the selected range.
                viz: bar
                spec:
                  specVersion: 1
                  dataSourceId: task
                  timeField: doneTime
                  measures:
                    - id: m1
                      fn: count
                  dimensions:
                    - id: d1
                      kind: terms
                      field: hubName
                      limit: 10
                  filters:
                    - id: f1
                      field: status
                      operator: eq
                      value: DONE
                defaultLayout:
                  w: 6
                  h: 6
        description: Custom widget
        required: true
      responses:
        '201':
          description: Created - the new custom widget
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                    example: true
                  code:
                    type: integer
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      schemaVersion:
                        type: integer
                        example: 1
                      name:
                        type: string
                        maxLength: 200
                        description: Literal display name, never translated
                      description:
                        type: string
                      category:
                        type: string
                        enum:
                          - custom
                        description: Always `custom`; set by the server
                      viz:
                        type: string
                        description: >-
                          Visualization: kpi, bar, line, donut, table, heatmap,
                          text, banner
                      spec:
                        type: object
                        description: >-
                          Neutral analytics query spec executed by POST
                          /analytics/query. See Run analytics query for the full
                          grammar.
                        properties:
                          specVersion:
                            type: integer
                          dataSourceId:
                            type: string
                          timeField:
                            type: string
                          measures:
                            type: array
                            items:
                              type: object
                              additionalProperties: true
                          dimensions:
                            type: array
                            items:
                              type: object
                              additionalProperties: true
                          filters:
                            type: array
                            items:
                              type: object
                              additionalProperties: true
                      composite:
                        type: object
                        description: >-
                          Multi-query widget `{kind, queries: {key: spec}}`;
                          `null` for ordinary widgets
                        additionalProperties: true
                      content:
                        type: object
                        description: '`{body}` for text and banner widgets; `null` otherwise'
                        properties:
                          body:
                            type: string
                            maxLength: 2000
                      defaultLayout:
                        type: object
                        description: Card placement on the grid
                        properties:
                          w:
                            type: integer
                            minimum: 2
                            description: Width in grid columns, 2 to `grid.columns`
                          h:
                            type: integer
                            minimum: 3
                            maximum: 18
                            description: >-
                              Height in rows, 3-18; `null` = as tall as its
                              content
                          x:
                            type: integer
                            minimum: 0
                            maximum: 48
                            description: >-
                              Optional column offset (free placement accepted,
                              not required)
                          'y':
                            type: integer
                            minimum: 0
                            description: Optional row offset
                      isCustom:
                        type: boolean
                        description: Always `true`; set by the server
                      _id:
                        type: string
                      organizationId:
                        type: string
                        description: The caller's organization id
                      createdBy:
                        type: string
                      updatedBy:
                        type: string
                      createdTime:
                        type: string
                        format: date-time
                      updatedTime:
                        type: string
                        format: date-time
              examples:
                response:
                  value:
                    status: true
                    code: 201
                    message: Widget has been added successfully.
                    data:
                      _id: 66fcd2a1e4b0a7d3c1f20c01
                      organizationId: 6650c2a1f3b9e4001c7d2a10
                      schemaVersion: 1
                      name: Done tasks per hub
                      description: Completed tasks per hub in the selected range.
                      category: custom
                      viz: bar
                      spec:
                        specVersion: 1
                        dataSourceId: task
                        timeField: doneTime
                        measures:
                          - id: m1
                            fn: count
                        dimensions:
                          - id: d1
                            kind: terms
                            field: hubName
                            limit: 10
                        filters:
                          - id: f1
                            field: status
                            operator: eq
                            value: DONE
                      composite: null
                      content: null
                      defaultLayout:
                        w: 6
                        h: 6
                      isCustom: true
                      createdBy: planner@example.com
                      updatedBy: planner@example.com
                      createdTime: '2026-10-02T03:00:00.000000Z'
                      updatedTime: '2026-10-02T03:00:00.000000Z'
        '400':
          description: >-
            Bad Request - `dydsh-001` `name`, `viz` or `spec` missing,
            `dydsh-009` length or range, `dydsh-011` `spec` or `defaultLayout`
            is not an object, `dydsh-014` `viz` is not an allowed chart type,
            `spec` carries its own `timeRange`, `spec` fails the analytics
            validator (`error` names the rule, for example
            `filter_value_missing`, `filter_value_invalid` or
            `bucket_estimate_exceeded`), or `spec` names a Flow (`flowId`
            filter, `eq` or `in`) that is not a live Flow of the organization
            (no `error`). A `spec` over the cost limit on a 31-day window, for
            example a per-minute `dateHistogram`, is `dydsh-014` with
            `error.code` `bucket_estimate_exceeded` (example). Time measures and
            dimensions follow the same rules as Run analytics query: an unknown
            duration pair, age field, bucket preset or dimension kind, `missing:
            "zero"` on `avgDuration`/`avgAge`/`doneShare`, a field on
            `doneShare`, a `granularity` on `ageBucket`/`hourOfDay`/`dayOfWeek`,
            two date groupings, or `cardinality`/`avg` under
            `hourOfDay`/`dayOfWeek` is `400 dydsh-014` with the message "The
            widget query is not valid: "; `<reason>` is English technical text
            and `error.code` names the rule (for example
            `duration_pair_unknown`, `bucket_preset_unknown`,
            `fold_measure_unsupported`). Data sources: a `spec.dataSourceId`
            that does not resolve for the organization (a `task:<flowId>` of
            another organization, a deleted or system flow, a
            `data:<dataTypeId>` that is unknown, of another organization,
            deleted or external) is `400 dydsh-014`; so is a spec on a Data
            source naming a field the data type does not offer (absent, not
            stored as keyword/number/date, or no record has a value yet), and an
            hourly grouping (`minute`/`hour` `dateHistogram` or `hourOfDay`) on
            a Data `dateTime` field without a `range` filter on it of at most 31
            days (`error.code` `time_grouping_needs_range`) or on a `date` field
            of any source (`error.code` `time_grouping_date_only`); a bare
            24-hex id is `400 dydsh-014` with `error.code`
            `data_source_id_invalid`. From / To follows Run analytics query
            (`duration_fields_same`, `duration_field_not_date`,
            `duration_pair_conflict`). A `spec` that names a retired Task field
            (`docId`, `organizationName`, `organizationEmail`, `hubCountry`,
            `hubLat`, `hubLng`, `year`) is `dydsh-014`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                    example: false
                  message:
                    type: string
                    description: Human-readable reason
                  failedCode:
                    type: string
                    description: Structured error code, see Status Codes
                  error:
                    type: object
                    description: >-
                      Present when the spec was refused by the analytics
                      validator
                    properties:
                      code:
                        type: string
                        description: Analytics rule, for example `field_unknown`
                      message:
                        type: string
                      field:
                        type: string
                        description: The offending field, when there is one
              examples:
                response:
                  value:
                    status: false
                    message: >-
                      The widget query is not valid: This combination of
                      dimensions produces about 44640 groups, but the limit is
                      5000. Narrow the time range or reduce the bucket sizes.
                    error:
                      code: bucket_estimate_exceeded
                      message: >-
                        This combination of dimensions produces about 44640
                        groups, but the limit is 5000. Narrow the time range or
                        reduce the bucket sizes.
                    failedCode: dydsh-014
        '401':
          description: Unauthorized - missing, invalid or expired access token
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                    example: false
                  message:
                    type: string
                    description: Human-readable reason
                  failedCode:
                    type: string
                    description: Structured error code, see Status Codes
              examples:
                response:
                  value:
                    status: false
                    message: Unauthenticated.
                    failedCode: auth-034
        '403':
          description: Forbidden - the caller's role lacks `add/dashboard`
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                    example: false
                  message:
                    type: string
                    description: Human-readable reason
                  failedCode:
                    type: string
                    description: Structured error code, see Status Codes
              examples:
                response:
                  value:
                    status: false
                    message: >-
                      Access denied. You do not have permission for add
                      dashboard. Please contact your admin to request access.
                    failedCode: sys-035
        '500':
          description: Server Error - unexpected exception, logged server-side
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                    example: false
                  message:
                    type: string
                    description: Human-readable reason
                  failedCode:
                    type: string
                    description: Structured error code, see Status Codes
                  code:
                    type: integer
                    description: >-
                      HTTP status repeated in the body (repository failures
                      only)
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                    description: Empty on most failures
              examples:
                response:
                  value:
                    status: false
                    code: 500
                    message: something_wrong
                    failedCode: dydsh-045
      security:
        - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Use a valid Bearer token to authenticate.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.