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:
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:
{
"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 URLFULLSEND_GCP_REGION- GCP region for Vertex AIOTEL_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) providerFULLSEND_GCP_PROJECT_ID- GCP project IDOTEL_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
- Poll job runs your custom logic to query an external system and builds a dispatch matrix
- Harness job calls
reusable-dispatch.ymlwith the pre-computed matrix reusable-dispatch.ymlskips the routing and dispatch steps (see architecture.md for the standard dispatch flow), directly invokingharness-runwith your matrix- 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 agentspackages: read- needed by code/fix agentsactions: 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.
