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

# Declares what happens when new rows land in a dataset: invoke an action on


> each new row, and optionally record the result in another dataset. The declaration replaces whatever was previously declared under the same name; enabled false removes it.




## OpenAPI

````yaml /api/openapi.json put /v1/dataset-row-actions
openapi: 3.0.0
info:
  title: Erdo API
  description: >-
    Erdo's REST API: query and write datasets, run agents, manage threads,
    integrations, pages, evals, workstreams, and experiments. Authenticate with
    a Bearer API key (erdo_api_...) or scoped token (erdo_token_...).
  version: '2026-07-27'
servers:
  - url: https://api.erdo.ai
    description: Production
security:
  - bearerAuth: []
paths:
  /v1/dataset-row-actions:
    put:
      summary: >
        Declares what happens when new rows land in a dataset: invoke an action
        on
      description: >
        each new row, and optionally record the result in another dataset. The
        declaration replaces whatever was previously declared under the same
        name; enabled false removes it.
      operationId: PUT:mcp.SetDatasetRowActionAPI
      requestBody:
        content:
          application/json:
            schema:
              properties:
                dataset:
                  description: |
                    including ones created later.
                  title: >
                    dataset is the slug to watch. Every pipeline that writes it
                    is covered,
                  type: string
                destination:
                  $ref: '#/components/schemas/mcp.RowActionDestination'
                enabled:
                  description: >
                    than pausing it, so declaring it again later starts from the
                    rows arriving then instead of working through everything
                    that accumulated in between.
                  title: >
                    enabled defaults to true. Passing false removes the
                    declaration rather
                  type: boolean
                key_column:
                  description: >
                    this dataset upserts on. It is how a row being written a
                    second time is recognised as the same row rather than a new
                    one. Normally detected; set it when the dataset identifies a
                    row unconventionally.
                  title: >
                    key_column names the column identifying a row — the one
                    whatever writes
                  type: string
                name:
                  description: >
                    dataset can both alert a desk and enrich a record without
                    either replacing the other. Lowercase letters, digits,
                    hyphens and underscores.
                  title: >
                    name distinguishes this declaration from others on the same
                    dataset, so a
                  type: string
                on_update:
                  description: >
                    written again: "skip" (the default) leaves it alone, and
                    "act" takes it through the steps again. Default to skip
                    unless a later write genuinely changes the answer — acting
                    again invokes the actions again, and pays for them again.
                  title: >
                    on_update says what happens when a row that has already been
                    acted on is
                  type: string
                steps:
                  description: >
                    once an earlier action has answered, and a script always
                    runs. A step whose templated input resolves to nothing
                    cannot be built for that row, so the next one is tried
                    instead.
                  items:
                    $ref: '#/components/schemas/mcp.RowActionStep'
                  title: >
                    steps is ordered. Each new row is taken through them: an
                    action is skipped
                  type: array
                timestamp_column:
                  description: >
                    detected; set it when the dataset names that column
                    unconventionally.
                  title: >
                    timestamp_column overrides which column marks a row's
                    arrival. Normally
                  type: string
              type: object
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  dataset:
                    type: string
                  destination:
                    $ref: '#/components/schemas/mcp.RowActionDestination'
                  enabled:
                    type: boolean
                  job_id:
                    description: |
                      history can be inspected through the automation surfaces.
                    title: >
                      job_id names the automation implementing this declaration,
                      so its run
                    type: string
                  key_column:
                    type: string
                  name:
                    type: string
                  on_update:
                    type: string
                  steps:
                    items:
                      $ref: '#/components/schemas/mcp.RowActionStep'
                    type: array
                  timestamp_column:
                    type: string
                type: object
          description: Success response
        default:
          $ref: '#/components/responses/APIError'
components:
  schemas:
    mcp.RowActionDestination:
      description: >
        normal: a step that sends an email or files a ticket has done its work
        by running, and has nothing to store.
      properties:
        columns:
          additionalProperties:
            type: string
          description: |
            row's own columns plus {{result...}}, {{row.column}} and {{now}}.
          title: >
            columns maps each destination column to a template, resolved against
            the
          type: object
        dataset:
          description: >
            dataset: writes there would trigger the automation again, and a
            keyed write would overwrite each source row with a partial one.
          title: |
            dataset is where result rows are written. It must not be the source
          type: string
        key_column:
          description: >
            subject twice replaces what was known rather than appending a rival
            copy. A row whose key renders empty is not written. Omit it to
            append.
          title: >
            key_column makes the write an upsert on that column, so acting on
            the same
          type: string
      title: >
        Where to record what the steps returned. Omitting a destination entirely
        is
      type: object
    mcp.RowActionStep:
      description: >
        OR a script to run, never both. Steps are taken in order: an action is
        skipped once an earlier action has answered, which is how a fallback is
        expressed, while a script always runs, because judging or shaping what
        the steps before it found costs nothing.
      properties:
        app:
          description: |
            connected integration. Omit it for a script step.
          title: |
            app names the app an action belongs to — "erdo", or the slug of a
          type: string
        fallback:
          description: >
            only when nothing has answered yet. Steps otherwise run in order,
            each seeing what the ones before it produced, so leave this off
            unless the step exists to cover an earlier one drawing a blank.
          title: >
            fallback marks this step as an alternative to the ones before it: it
            runs
          type: boolean
        for_each:
          description: >
            {{placeholder}} resolving to one. The action is invoked once per
            element, with {{item}} and {{item\_index}} available in its input,
            and the step's result is the list of what came back. Every
            invocation counts against the run's budget.
          title: |
            for_each fans the action out over a list: a JSON array, or one
          type: object
        found_when:
          description: >
            found anything, e.g. "found". It exists because an action that found
            nothing rarely returns nothing: it returns an envelope saying so,
            and Erdo appends ok:true to every native action's result, so "did
            this answer" and "what should be recorded" are different questions.
            Without it a miss reads as a hit and the step behind it is
            unreachable. Defaults to "the extracted result is not empty".
          title: >
            found_when is a dotted path into the action's result that says
            whether it
          type: string
        input:
          description: >
            may contain {{column}} placeholders resolved against the row;
            everything else passes through untouched, so a numeric setting stays
            a number.
          title: >
            input carries the action's own parameters, as a JSON object. String
            values
          type: object
        key:
          title: >
            key names the action within that app, e.g. "send_email" or
            "web_search".
          type: string
        result_path:
          description: >
            Many actions wrap their answer in an envelope that echoes the
            request, so a search that found nothing still comes back non-empty;
            without a path every such action looks like it succeeded and the
            next step is never tried. It also becomes what {{result}} refers to.
            Defaults to the whole result.
          title: >
            result_path is a dotted path into the action's result, e.g.
            "results.0".
          type: string
        script:
          description: >
            is a function body receiving (row, result, steps, run): the row's
            columns, the answer the steps before it produced, their individual
            results, and {total, index, rows, budget} describing the run. What
            it returns becomes this step's result; returning nothing clears the
            answer, so a finding it judged unusable falls through to the next
            action instead of being recorded. It may also return a \`note\`
            explaining the decision, which is reported on the run result and
            never counts as an answer. A script shapes data: it cannot invoke
            actions or read datasets, because a declaration has to say what it
            calls and where it writes.
          title: >
            script is JavaScript run against the row instead of invoking an
            action. It
          type: string
      title: >
        One step of a declaration. A step is EITHER an action to invoke (app and
        key)
      type: object
  responses:
    APIError:
      content:
        application/json:
          schema:
            externalDocs:
              url: https://pkg.go.dev/encore.dev/beta/errs#Error
            properties:
              code:
                description: Error code
                example: not_found
                externalDocs:
                  url: https://pkg.go.dev/encore.dev/beta/errs#ErrCode
                type: string
              details:
                description: Error details
                type: object
              message:
                description: Error message
                type: string
            title: APIError
            type: object
      description: Error response
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).

````