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

# Preview mass-action plan

> Validate a whole bulk plan without running anything and mint the consent token create_mass_action consumes. ALWAYS the first call of a bulk dispatch.

Validates in one pass, reporting all findings at once: plan shape (1..3 steps), step-eligibility of each tool, scope shape and size (1..100), the generate-scope rule (step 1 must mint an object) and the send-class schedule mandate. A tool outside the step vocabulary comes back 422 validation_failed with error.field_errors["plan.steps.{i}.tool"] = ["not_step_eligible: ..."], naming the authorable set so the plan is repairable in one turn. Nothing is persisted, charged or created.

On success the result carries preview (items_count, steps_per_item, credits_estimate, dangerous_steps, eta, warnings), commit_token and expires_at. Show the preview to the user, then pass the token to create_mass_action UNCHANGED with the exact same inputs: it is an HMAC over them plus the caller, so any edit invalidates it (422) and needs a fresh preview. Tokens live 15 minutes.

Contract:
- MCP tool `preview_mass_action`, registry package `mcp.orchestration/mass_actions`, mount `orchestration.mass_actions`.
- Operation `action`, response envelope `action`.
- Flags: read only.



## OpenAPI

````yaml /api-reference/orchestration/openapi.yaml post /api/mass-actions/preview
openapi: 3.0.3
info:
  title: 'GTM API public contract: gtm.service.orchestration'
  description: >-
    The cross-service execution plane: the platform-wide webhook registry and
    delivery log, plus mass actions (preview, commit, pace, pause, resume,
    canary) and their per-item child rows.


    GENERATED. This document is projected from the Zod MCP tool registry in
    `product/mcp/gtm.mcp` (one tool per public endpoint, 1:1). Do not edit it by
    hand; edit the tool definition and regenerate with `pnpm openapi:public`.


    Surface: the public `/api` contract of `gtm.service.orchestration`, 21
    operations. Internal (`/internal`) and health endpoints are deliberately
    absent; the code-faithful spec that documents those lives in
    `product/openapi/gtm.openapi.tech`.


    Conventions:

    - Auth is a bearer JWT, optionally narrowed by the `Team-SID` header.

    - Every success body is an MCP envelope: `success: true` plus one typed
    `operation` shape (`search`, `get`, `create`, `update`, `delete`, `metrics`,
    `group_by`, `action`), and a `meta` block with `trace_id` for support.

    - Every failure is the same `McpError` envelope with a code from a fixed
    16-code taxonomy, so a client maps errors once.

    - Lists page with `page_size` (0 to 500, default 50) plus an opaque forward
    `cursor`; `page_size: 0` returns counts only.

    - On `GET` and `DELETE`, object-valued query parameters (`filter`, `sort`)
    travel as JSON text and array-valued ones repeat as `name[]=value`.

    - The MCP-only `_meta` field (usage analytics) never reaches the backend and
    is not part of this contract.
  version: '1.0'
  contact:
    name: GTM API
    url: https://gtm-api.com
    email: support@gtm-api.com
  license:
    name: Proprietary
    url: https://gtm-api.com/license
servers:
  - url: https://app.gtm-api.com/orchestration/v4
    description: Production, through the app.gtm-api.com gateway
  - url: http://localhost:8025
    description: Local Docker (gtm_orchestration_nginx_dev)
security:
  - BearerJwt: []
    TeamSid: []
tags:
  - name: mass_action_items
    description: >-
      Registry package `mcp.orchestration/mass_action_items`, served on MCP
      mount `orchestration.mass_actions`.
  - name: mass_actions
    description: >-
      Registry package `mcp.orchestration/mass_actions`, served on MCP mount
      `orchestration.mass_actions`.
  - name: webhook_logs
    description: >-
      Registry package `mcp.orchestration/webhook_logs`, served on MCP mount
      `orchestration.webhooks`.
  - name: webhooks
    description: >-
      Registry package `mcp.orchestration/webhooks`, served on MCP mount
      `orchestration.webhooks`.
paths:
  /api/mass-actions/preview:
    post:
      tags:
        - mass_actions
      summary: Preview mass-action plan
      description: >-
        Validate a whole bulk plan without running anything and mint the consent
        token create_mass_action consumes. ALWAYS the first call of a bulk
        dispatch.


        Validates in one pass, reporting all findings at once: plan shape (1..3
        steps), step-eligibility of each tool, scope shape and size (1..100),
        the generate-scope rule (step 1 must mint an object) and the send-class
        schedule mandate. A tool outside the step vocabulary comes back 422
        validation_failed with error.field_errors["plan.steps.{i}.tool"] =
        ["not_step_eligible: ..."], naming the authorable set so the plan is
        repairable in one turn. Nothing is persisted, charged or created.


        On success the result carries preview (items_count, steps_per_item,
        credits_estimate, dangerous_steps, eta, warnings), commit_token and
        expires_at. Show the preview to the user, then pass the token to
        create_mass_action UNCHANGED with the exact same inputs: it is an HMAC
        over them plus the caller, so any edit invalidates it (422) and needs a
        fresh preview. Tokens live 15 minutes.


        Contract:

        - MCP tool `preview_mass_action`, registry package
        `mcp.orchestration/mass_actions`, mount `orchestration.mass_actions`.

        - Operation `action`, response envelope `action`.

        - Flags: read only.
      operationId: preview_mass_action
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PreviewMassActionRequest'
      responses:
        '200':
          description: '`action` success envelope.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewMassActionResponse'
        4XX:
          $ref: '#/components/responses/McpClientError'
        5XX:
          $ref: '#/components/responses/McpServerError'
components:
  schemas:
    PreviewMassActionRequest:
      type: object
      description: Request body of `preview_mass_action`.
      properties:
        title:
          type: string
          nullable: true
          maxLength: 255
          description: >-
            Human label for the run, shown in the list view and on the consent
            surface.
        target_entity:
          type: string
          maxLength: 128
          description: >-
            Kebab-plural entity family the plan's steps operate on, e.g.
            'linkedin-connection-requests', 'linkedin-posting' or
            'email-messages'. The compatibility anchor a linked auto-scrape
            checks against.
        scope:
          anyOf:
            - type: object
              properties:
                kind:
                  type: string
                  enum:
                    - objects
                object_sids:
                  type: array
                  items:
                    type: string
                    minLength: 18
                    maxLength: 18
                  minItems: 1
                  maxItems: 100
                  description: Existing rows of the target_entity family, 1..100.
              required:
                - kind
                - object_sids
            - type: object
              properties:
                kind:
                  type: string
                  enum:
                    - targets
                targets:
                  type: array
                  items:
                    type: object
                    additionalProperties: {}
                  minItems: 1
                  maxItems: 100
                  description: >-
                    1..100 payload-kind identities (send-class); per-item
                    params, shape owned by the target entity.
              required:
                - kind
                - targets
            - type: object
              properties:
                kind:
                  type: string
                  enum:
                    - generate
                count:
                  type: integer
                  minimum: 1
                  maximum: 100
                  description: >-
                    N slot items with no pre-existing object; step 1 must be a
                    creates: verb.
              required:
                - kind
                - count
            - type: object
              properties:
                kind:
                  type: string
                  enum:
                    - none
              required:
                - kind
              description: >-
                Empty: a STANDING run at 0 items that an auto-scrape appends
                into over time.
          description: >-
            What the run enrols: existing rows (objects), payload identities
            (targets), generated slots (generate), or nothing yet (none, a
            standing run an auto-scrape feeds).
        plan:
          type: object
          properties:
            steps:
              type: array
              items:
                type: object
                properties:
                  tool:
                    anyOf:
                      - type: string
                        enum:
                          - >-
                            linkedin-connection-requests.send-linkedin-connection-request
                          - linkedin-posting.react
                          - email-messages.send
                      - type: string
                        enum:
                          - linkedin-connection-requests.send
                    description: >-
                      Dotted verb '{entity-kebab-plural}.{verb}'. Only these are
                      step-eligible today; anything else fails preview with 422
                      not_step_eligible on field plan.steps.{i}.tool.
                      'linkedin-connection-requests.send' is a legacy alias of
                      the send-linkedin-connection-request case, accepted but
                      not the spelling to author.
                  args:
                    type: object
                    additionalProperties: {}
                    description: >-
                      The verb's own arguments, EXCLUDING the target: the target
                      is injected per item from the object cursor or the item
                      payload. Shared across every item of the run.
                required:
                  - tool
              minItems: 1
              maxItems: 3
              description: >-
                1..3 steps, run in order per item. Step ids are server-assigned
                1-based ordinals; do not send them. Longer intents split into
                sequential mass-actions.
          required:
            - steps
        schedule:
          type: object
          nullable: true
          properties:
            interval_seconds_min:
              type: integer
              minimum: 30
              maximum: 86400
            interval_seconds_max:
              type: integer
              minimum: 30
              maximum: 86400
              description: '>= interval_seconds_min.'
          required:
            - interval_seconds_min
            - interval_seconds_max
          description: >-
            Omit for an ASAP drain. Required when the plan carries a send-class
            step, else 422 schedule_required.
        canary_mode:
          type: string
          enum:
            - none
            - first_item
          description: >-
            Default 'first_item': only item 1 dispatches until it succeeds, so a
            wrong plan burns one target instead of all of them.
      required:
        - target_entity
        - scope
        - plan
    PreviewMassActionResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        operation:
          type: string
          enum:
            - action
        action:
          type: string
          description: kebab-case verb; matches the route segment.
        item: {}
        result:
          type: object
          properties:
            preview:
              type: object
              properties:
                items_count:
                  type: number
                steps_per_item:
                  type: number
                credits_estimate:
                  type: number
                dangerous_steps:
                  type: array
                  items:
                    type: object
                    properties:
                      step_id:
                        type: number
                      tool:
                        type: string
                    required:
                      - step_id
                      - tool
                eta:
                  type: object
                  properties:
                    starts:
                      type: string
                    estimated_completion_at:
                      type: string
                      nullable: true
                  required:
                    - starts
                    - estimated_completion_at
                warnings:
                  type: array
                  items:
                    type: string
              required:
                - items_count
                - steps_per_item
                - credits_estimate
                - dangerous_steps
                - eta
                - warnings
            commit_token:
              type: string
            expires_at:
              type: string
          required:
            - preview
            - commit_token
            - expires_at
        meta:
          type: object
          properties:
            trace_id:
              type: string
              description: UUID v7; same 128-bit value as the X-Trace-Id header.
            span_id:
              type: string
              pattern: ^[0-9a-f]{16}$
              description: 16 hex chars, root span of this request.
            timestamp:
              type: string
              description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
            duration_ms:
              type: integer
              minimum: 0
              description: Server-side wall clock.
            debug_url:
              type: string
              description: Deep link to the post-call analysis UI.
          required:
            - trace_id
            - span_id
            - timestamp
            - duration_ms
            - debug_url
        credits:
          type: object
          properties:
            charged:
              type: integer
              minimum: 0
              description: Credits debited for THIS call (0 on own-account / cache hit).
            reason:
              type: string
              nullable: true
              enum:
                - infra_pool
                - limit_fallback
            executed_on:
              type: string
              enum:
                - own_account
                - infra_pool
            balance_after:
              type: integer
              nullable: true
              minimum: 0
              description: >-
                Team balance after the debit; null when the ledger was
                untouched.
          required:
            - charged
            - reason
            - executed_on
            - balance_after
      required:
        - success
        - operation
        - action
        - item
        - result
        - meta
    McpError:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - validation_failed
                - nothing_to_update
                - not_found
                - relation_not_found
                - invalid_transition
                - limit_exceeded
                - payment_required
                - duplicate_rejected
                - conflict
                - delete_blocked
                - unauthorized
                - forbidden
                - rate_limited
                - internal_error
                - service_unavailable
                - not_implemented
            message:
              type: string
            recoverable:
              type: boolean
            suggestion:
              type: string
            field_errors:
              type: object
              additionalProperties:
                type: array
                items:
                  anyOf:
                    - type: string
                    - type: object
                      properties:
                        rule:
                          type: string
                        message:
                          type: string
                      required:
                        - rule
                        - message
            blockers:
              type: array
              items:
                type: object
                properties:
                  type:
                    type: string
                    description: >-
                      Machine-readable blocker type (active_flow, pending_tasks,
                      …).
                  severity:
                    type: string
                    enum:
                      - hard
                      - soft
                    description: >-
                      hard = external action required; soft = acknowledge is
                      enough.
                  description:
                    type: string
                  entity_sid:
                    type: string
                    nullable: true
                  count:
                    type: integer
                  resolution:
                    type: string
                    description: 'Hard: tool name to call. Soft: code for acknowledge[].'
                  resolution_hint:
                    type: string
                required:
                  - type
                  - severity
                  - description
                  - entity_sid
                  - resolution
                  - resolution_hint
            context:
              type: object
              additionalProperties: {}
          required:
            - code
            - message
            - recoverable
        meta:
          type: object
          properties:
            trace_id:
              type: string
              description: UUID v7; same 128-bit value as the X-Trace-Id header.
            span_id:
              type: string
              pattern: ^[0-9a-f]{16}$
              description: 16 hex chars, root span of this request.
            timestamp:
              type: string
              description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
            duration_ms:
              type: integer
              minimum: 0
              description: Server-side wall clock.
            debug_url:
              type: string
              description: Deep link to the post-call analysis UI.
          required:
            - trace_id
            - span_id
            - timestamp
            - duration_ms
            - debug_url
      required:
        - success
        - error
  responses:
    McpClientError:
      description: >-
        MCP error envelope. `error.code` is one of validation_failed,
        nothing_to_update, not_found, relation_not_found, invalid_transition,
        limit_exceeded, payment_required, duplicate_rejected, conflict,
        delete_blocked, unauthorized, forbidden, rate_limited, not_implemented.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/McpError'
    McpServerError:
      description: >-
        MCP error envelope with `error.code` internal_error or
        service_unavailable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/McpError'
  securitySchemes:
    BearerJwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Access token issued by gtm.service.id. Its `access_identity` claim
        carries `team_sid`, `actor_sid` and `actor_type`, and that team scope is
        authoritative.
    TeamSid:
      type: apiKey
      in: header
      name: Team-SID
      description: >-
        Team scope for tokens that do not carry one. Ignored when the token
        already names a team.

````