Netlify Agent Runners

Start Netlify Agent Runners agent runs from GitHub issues and pull requests. Just mention @netlify and the selected agent builds your site.

Runner execution uses the published nax-agent-runner-sdk and lands changes as an open pull request; this action never merges automatically.

- uses: netlify-labs/agent-runner-action@v1
  with:
    netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
    netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }}

Setup

1

Install the GitHub App

Install netlify-coding on your repository. This lets the agent create commits and pull requests.

2

Create a Netlify site

Link your repo to a Netlify site. Note the Site ID from Site configuration > General.

netlify init
3

Add repository secrets

Go to Settings > Secrets and variables > Actions and add:

SecretDescription
NETLIFY_AUTH_TOKENYour Netlify personal access token
NETLIFY_SITE_IDYour Netlify site ID
4

Add the workflow

Create .github/workflows/netlify-agents.yml:

name: Netlify Agent Runners

on:
  workflow_dispatch:
    inputs:
      trigger_text:
        description: 'Prompt for the agent run'
        required: true
        type: string
        default: '@netlify'
      actor:
        description: 'Actor triggering the agent'
        required: true
        type: string
      agent:
        description: 'Agent to use'
        required: false
        type: choice
        options:
          - codex
          - claude
          - gemini
        default: 'codex'
  pull_request_target:
    types: [opened, reopened]
  pull_request_review_comment:
    types: [created]
  pull_request_review:
    types: [submitted, edited]
  issues:
    types: [opened, assigned, edited]
  issue_comment:
    types: [created, edited]

concurrency:
  group: ${{ (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && github.event.comment.body == '@netlify stop' && format('netlify-stop-{0}', github.run_id) || format('netlify-{0}-{1}', github.repository, github.event.pull_request.number || github.event.issue.number || inputs.recover_thread || github.run_id) }}
  cancel-in-progress: false

jobs:
  netlify-agent:
    # Skip bot senders early to avoid burning Actions minutes
    if: >-
      github.event_name == 'workflow_dispatch' ||
      (
        github.event.sender.login != 'github-actions[bot]' &&
        github.event.sender.login != 'netlify-coding[bot]' &&
        github.event.sender.login != 'netlify[bot]'
      )
    runs-on: ubuntu-latest
    timeout-minutes: 25
    permissions:
      contents: write
      pull-requests: write
      issues: write
    steps:
      - uses: netlify-labs/agent-runner-action@v1
        with:
          netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }}
          # preflight-only: 'false' # Validate setup and skip agent execution

Usage

From an issue

Create an issue with @netlify in the body:

@netlify Build a landing page for a coffee shop with a menu and contact form

Choose an agent

Specify claude, codex, gemini, or opencode after @netlify:

@netlify claude Add a dark mode toggle
@netlify codex Make the hero section responsive
@netlify gemini Add a testimonials carousel

Default agent is codex. You can change the default with the default-agent input.

Choose a model and effort

After @netlify, name an agent, a model, and an effort level, in that order. Each is optional, and a model picks its own agent:

@netlify fable high Refactor the checkout flow
@netlify claude fable high Refactor the checkout flow
@netlify-fable high Refactor the checkout flow
@netlify codex sol medium Add retry logic
@netlify claude high Fix the header

Claude models: fable, opus, opus-4.8, sonnet, haiku. Codex: codex sol, codex terra, codex luna, codex mini. Gemini: gemini pro, gemini flash, gemini flash-lite. OpenCode: kimi, kimi-code, glm, deepseek, deepseek-flash, grok, minimax. Exact model IDs and model:<id> also work.

Effort is low, medium, or high (some OpenCode models also take max; Kimi K2.7 Code and MiniMax M3 take none), placed after an agent or model and followed by a space, :, ,, or the end of the line. effort:<level> anywhere on the @netlify line takes precedence, and effort:auto forces Auto. Anything left out is chosen by the backend (Auto) unless you set default-model-id or default-effort.

Follow-up prompts

After the first run creates a PR, add follow-up @netlify comments on the PR to iterate. The agent continues from where it left off.

Execution modes

dry-run and preflight-only are different controls:

- uses: netlify-labs/agent-runner-action@v1
  with:
    netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
    netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }}
    preflight-only: 'true' # setup validation only

- uses: netlify-labs/agent-runner-action@v1
  with:
    netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
    netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }}
    dry-run: 'true' # run agent preview, skip commits/PR

Typo-tolerant

Common typos are recognized: @nelify, @netlfy, @netify, @netlif, @netfly. Aliases like @netlify-agent and @netlify-ai also work.

Inputs

InputRequiredDefaultDescription
netlify-auth-tokenYes—Netlify personal access token
netlify-site-idYes—Netlify site ID
netlify-filterNo''Deprecated compatibility input. SDK dispatch uses the exact netlify-site-id.
github-tokenNogithub.tokenGitHub token for API calls
allowed-usersNo''Comma-separated usernames allowed to trigger
default-agentNocodexDefault agent (claude, codex, gemini, opencode)
default-modelNocodexBackward-compatible alias for default-agent
default-model-idNo''Default model ID or alias (e.g. claude-fable-5, fable). Empty or auto lets the backend choose
default-effortNo''Default effort level (low, medium, high). Empty or auto lets the backend choose
manage-labelsNofalseAuto-create and apply labels on runs
protected-pathsNodefaultFiles to flag for review when a run changes them (globs; default = built-in list, none disables, !pattern excludes)
flag-new-top-levelNotrueAlso flag files added at the repository root and new top-level folders
protected-paths-actionNocommentcomment (always on), plus optionally label and/or draft
scope-instructionsNodefaultGuidance appended to every agent prompt; none disables
simulate-orphanNofalseTest-only (canary): exit after the run checkpoint is written
operationNotriggertrigger or recover-scan (scheduled recovery)
recover-workflowNonetlify-agents.ymlFor recover-scan: the main workflow file to dispatch
recover-lookback-hoursNo48For recover-scan: only check recently updated threads
recover-max-itemsNo50For recover-scan: maximum threads to check
attach-pr-diffNotrueOn PRs, attach the full diff against the base branch to the agent session (.netlify/assets/); the runner can't git diff against the base itself
dry-runNofalseStart an agent run but skip commit/PR creation
preflight-onlyNofalseValidate setup and exit without creating/resuming an agent run
job-timeout-minutesNo''The job's timeout-minutes; the agent limit is shortened to fit inside it
timeout-minutesNo10Max minutes to wait for agent
netlify-cli-versionNo24.8.1Netlify CLI version to install
debugNofalseEnable debug API logging
timezoneNoAmerica/Los_AngelesTimezone used for date/time rendering in comments

Outputs

Use these in subsequent workflow steps for custom automation:

OutputDescription
agent-idAgent run ID
outcomesuccess, failure, or timeout
agent-resultAgent result summary text
agent-pr-urlPull request URL (if created)
agent-deploy-urlDeploy preview URL
agentAgent that was used
modelBackward-compatible alias for agent
model-idModel ID that was requested (empty when the backend chose Auto)
effortEffort level that was requested (empty when the backend chose Auto)
trigger-textCleaned trigger prompt
is-prtrue / false
issue-numberIssue or PR number
is-dry-runWhether the run used preview mode (true / false)
preflight-okWhether preflight validation passed (true / false)
preflight-jsonSerialized preflight result payload (ok, checks, warnings, failures)
preflight-summaryHuman-readable preflight status summary
should-continueWhether execution should continue into agent runtime
failure-categoryPreflight/runtime taxonomy category when available
failure-stagePreflight/runtime failure stage when available
scope-flagsJSON array of files the scope check flagged
scope-flag-countNumber of files the scope check flagged
agent-errorSanitized runtime error summary emitted by agent orchestration

Example: run tests after agent

steps:
  - uses: netlify-labs/agent-runner-action@v1
    id: agent
    with:
      netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
      netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }}

  - name: Run tests on agent PR
    if: steps.agent.outputs.outcome == 'success'
    run: echo "PR created at ${{ steps.agent.outputs.agent-pr-url }}"

Asking questions

Ask mode answers a question about the code in a comment, without changing files or opening a PR. Use @netlify-ask, @netlify ask: (the colon is required), or mode:ask right after the mention: for example @netlify-ask fable high how are webhooks retried?. A plain "ask" isn't enough, because @netlify ask the user to confirm… is a build request. The answer appears as a result comment marked 💬 Answered. On a PR, the agent reads the PR branch, and a later @netlify request continues with the same context.

Scope guard

After a successful run, the action checks which files that run changed and adds a ⚠️ Review these changes table to the result comment (and to the PR, for runs started from an issue) when it touched workflows, netlify.toml, root *.toml files, lockfiles, AGENTS.md/CLAUDE.md, .env files, new root files, or new top-level folders. Files the request names are listed as requested instead. It never blocks a run.

Configure it with protected-paths (globs; a pattern without / matches only at the root; default, none, and !pattern are supported) and escalate with protected-paths-action: comment,label,draft. By default, scope-instructions also tells agents to report unrelated build or deploy failures instead of working around them.

Run checkpoints and stopping

When a run starts, the status comment links to the live agent run and stores an encrypted checkpoint. The checkpoint is sealed with a key derived from your Netlify token, bound to this thread and runner, and never contains the prompt or site ID in plain text. Cancelling the workflow stops the agent run. Set job-timeout-minutes to your job's timeout-minutes so the agent's own limit fits inside the job. Transient API errors while waiting are retried until the run's time limit.

To stop a run, add a comment containing only @netlify stop. The action stops the agent right away, and the run's comments say "Stopped by @you" instead of "failed". This needs the stop-aware concurrency group from the current templates, so the stop doesn't wait behind the run it stops.

Versioning

Releases are tagged vX.Y.Z, and v1 always points at the latest v1.Y.Z release. Use @v1 to follow minor and patch releases automatically, or pin a release commit with a version comment (@<sha> # v1.2.0) and let Dependabot's github-actions updates propose upgrades.

Breaking changes (removed or renamed inputs and outputs, incompatible comment markers, or mention syntax that selects something different) only ship in a new major version. Release notes call out behavior changes and any workflow-file updates needed to use new features.

How it Works

  1. You mention @netlify in an issue, PR, or comment
  2. The action reacts with eyes emoji and posts an in-progress status comment
  3. It creates an agent run with your prompt
  4. The selected agent builds or modifies your site
  5. On completion, the action posts a full result comment, then updates the status comment with a short summary and a link to that result
  6. If triggered from an issue, a pull request is automatically created with the changes

Comments

Security

Troubleshooting

Missing NETLIFY_AUTH_TOKEN or NETLIFY_SITE_ID

Go to Settings > Secrets in your repo and add both secrets. Create a token here. Find your Site ID in the Netlify dashboard under Site configuration > General.

Preflight-only failed checks

Use preflight-summary and preflight-json outputs to identify failing checks. Common fixes: token/site ID mismatch, invalid default agent, non-positive timeout, or missing workflow permissions.

"The project can't be found"

The NETLIFY_SITE_ID secret points to a site that doesn't exist or that your token can't access. Verify the site ID in the Netlify dashboard and regenerate your token if needed.

Agent timed out

The default timeout is 10 minutes. For complex prompts, increase it with timeout-minutes: '15'. You can also break large tasks into smaller follow-up prompts.

"dry-run" still contacted Netlify

This is expected. dry-run skips commit and PR creation only. Use preflight-only: 'true' when you need validation with no agent run.

"Requested agent is not available"

The selected agent is temporarily unavailable. Try a different agent: @netlify claude, @netlify codex, or @netlify gemini.

Bot triggered the workflow

Add a job-level if: guard to skip bot senders. The example workflow includes this by default. See the setup section.

Node.js 20 deprecation warning

Update the action dependencies in action.yml to their latest versions (v5/v6/v8). The published action already uses Node 24-compatible versions.