Skip to content
By Ralph Bean, Barak Korren
Picture of Ralph Bean
Ralph Bean
Picture of Barak Korren
Barak Korren

Custom Poller Example ​

This guide shows how to create a custom poller in your own repository that invokes fullsend harness agents directly, bypassing the standard GitHub event trigger flow.

Use Case ​

Custom pollers are useful when you want to:

  • Poll external systems (Jira, Linear, Slack, etc.) on a schedule
  • Trigger harness agents based on custom logic
  • Reuse fullsend's harness infrastructure without duplicating workflow code

Example: Jira Polling Workflow ​

This example polls Jira for bugs and dispatches fullsend agents to process them:

yaml
name: Fullsend Jira Poll

on:
  schedule:
    - cron: '*/30 * * * *'  # every 30 minutes
  workflow_dispatch:

permissions:
  actions: write
  contents: write
  id-token: write
  issues: write
  packages: read
  pull-requests: write

jobs:
  poll:
    runs-on: ubuntu-24.04
    outputs:
      matrix: ${{ steps.dispatch.outputs.matrix }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install fullsend CLI
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh release download --repo fullsend-ai/fullsend \
            -p 'fullsend_*_linux_amd64.tar.gz' -O - | tar xz
          sudo mv fullsend /usr/local/bin/

      - name: Poll Jira and build dispatch matrix
        id: dispatch
        env:
          JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }}
          JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
          JIRA_BASE_URL: ${{ vars.JIRA_BASE_URL }}
          TARGET_REPO: ${{ github.repository }}
        run: |
          fullsend poll \
            --input-driver jira-poll \
            --jira-url "${JIRA_BASE_URL}" \
            --jira-project MYPROJECT \
            --jql 'project=MYPROJECT and statusCategory != Done and updated > -1week and type=Bug' \
            --target-repo "${TARGET_REPO}" \
            --output dispatches.json \
            --fullsend-dir .fullsend

          # Build GitHub Actions matrix format
          if ! jq -e 'length > 0' dispatches.json > /dev/null 2>&1; then
            echo 'matrix={"include":[]}' >> "${GITHUB_OUTPUT}"
            exit 0
          fi
          MATRIX=$(jq -c '{include: .}' dispatches.json)
          DELIM="MATRIX_$(openssl rand -hex 8)"
          {
            echo "matrix<<${DELIM}"
            printf '%s' "${MATRIX}"
            echo
            echo "${DELIM}"
          } >> "${GITHUB_OUTPUT}"

  harness:
    needs: poll
    uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@main
    with:
      matrix: ${{ needs.poll.outputs.matrix }}
      mint_url: ${{ vars.FULLSEND_MINT_URL }}
      gcp_region: ${{ vars.FULLSEND_GCP_REGION }}
      jira_base_url: ${{ vars.JIRA_BASE_URL }}
    secrets:
      FULLSEND_GCP_WIF_PROVIDER: ${{ secrets.FULLSEND_GCP_WIF_PROVIDER }}
      FULLSEND_GCP_PROJECT_ID: ${{ secrets.FULLSEND_GCP_PROJECT_ID }}
      OTEL_EXPORTER_OTLP_TRACES_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_TRACES_HEADERS }}
      OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
      JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }}
      JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}

Matrix Format ​

The matrix input (a GitHub Actions matrix strategy that runs parallel jobs) must follow the format produced by fullsend dispatch --output-driver gha-matrix:

json
{
  "include": [
    {
      "agent": "agent-name",
      "source_repo": "org/repo",
      "role": "harness",
      "event_payload": "{...}",
      "status_repo": "org/repo",
      "status_number": "123"
    }
  ]
}

Prerequisites ​

Your external repository needs these variables and secrets configured:

Variables:

  • FULLSEND_MINT_URL - Token mint service URL
  • FULLSEND_GCP_REGION - GCP region for Vertex AI
  • OTEL_EXPORTER_OTLP_ENDPOINT - OpenTelemetry endpoint (optional)
  • JIRA_BASE_URL - Jira instance URL (if using Jira agents)

Secrets:

  • FULLSEND_GCP_WIF_PROVIDER - GCP Workload Identity Federation (WIF) provider
  • FULLSEND_GCP_PROJECT_ID - GCP project ID
  • OTEL_EXPORTER_OTLP_TRACES_HEADERS - OTEL auth headers (optional)
  • JIRA_TOKEN - Jira API token (if using Jira agents)
  • JIRA_USER_EMAIL - Jira user email (if using Jira agents)

How It Works ​

  1. Poll job runs your custom logic to query an external system and builds a dispatch matrix
  2. Harness job calls reusable-dispatch.yml with the pre-computed matrix
  3. reusable-dispatch.yml skips the routing and dispatch steps (see architecture.md for the standard dispatch flow), directly invoking harness-run with your matrix
  4. Harness agents execute according to your matrix configuration

Permissions ​

The top-level permissions: block grants the maximum permissions required by reusable-dispatch.yml. Even though you're only running harness agents via the pre-computed matrix, GitHub validates that the caller grants sufficient permissions for all jobs in the reusable workflow (including code and fix jobs that won't actually run).

Required permissions:

  • contents: write - needed by code/fix agents
  • packages: read - needed by code/fix agents
  • actions: write, id-token: write, issues: write, pull-requests: write - needed by all agents

id-token: write lets GitHub Actions issue the job a short-lived OpenID Connect (OIDC) token. Fullsend uses this token to identify the calling workflow during dispatch and to mint short-lived GitHub App installation tokens for agents. Grant it in the caller workflow or on the job that calls reusable-dispatch.yml; the reusable workflow cannot add permissions that the caller did not grant. Keep the OIDC token inside the job. Do not copy it into workflow outputs, the dispatch matrix, or logs.

Authorization ​

When using a pre-computed matrix, the .fullsend/config.yaml agent-enablement checks are bypassed. The token mint service acts as the authorization boundary (see mint-administration.md) - ensure your mint service is properly configured to control which agents external callers can invoke.