Trigger a workflow from GitHub Actions
Drop a **`workflow_dispatch`** job into `.github/workflows/` — POST your intake with repo secrets, capture **`work_id`**, poll until **`completed`** or **`failed`** — same contract as [Trigger a workflow with an intake webhook](/tutorials/trigger-a-workflow-with-an-intake-webhook) but with **`GITHUB_RUN_ID`** correlation baked in.
Plus: three Admin-Agent passes — sanity-check YAML secret names vs intake host, decode a green POST + red poll split, and decide reusable workflow vs copy-paste when three repos need the same intake.
| Audience | Developers · Admins |
|---|---|
| Time | ~10 min |
| Prerequisites | Intake + curl fluency ([Trigger a workflow with an intake webhook](/tutorials/trigger-a-workflow-with-an-intake-webhook)). A runnable workflow ([Run a workflow](/tutorials/run-a-workflow)). Repo permission to add **Actions secrets**. Prefer **`team.`** keys for shared CI ([Create a shared Team API Key](/tutorials/create-a-team-api-key), [Pick personal or team API keys for real work](/tutorials/pick-personal-or-team-api-keys-for-real-work)). Hardening mindset ([Harden your intake webhooks](/tutorials/harden-your-intake-webhooks)). |
| You'll end up with | One committed workflow file that triggers Auxot from CI — secrets live in **GitHub → Settings → Secrets**, logs stay free of Bearer tokens, failures surface as non-zero exit after poll sees **`failed`**. |
When a tutorial shows italic text in quotation marks, it usually mirrors a label or helper string inside Auxot. Product copy changes between releases — if something reads differently in your workspace, trust what you see on screen.
Callouts with a Worth knowing gold accent are meant as must-read context before you move on. Blockquotes that open with Tip are lighter, optional depth.
Why this matters
Trigger a workflow with an intake webhook already proved 202 + work_id + GET poll from a laptop. Production glue moves that script into GitHub Actions so merges, releases, or manual workflow_dispatch kicks fire Auxot without a human holding curl.
Same ingress rules apply:
POST /v1/intake/{INTAKE_ID}: Beareruser.orteam.(Intake Webhooks).- Poll:
GET /v1/intake/{INTAKE_ID}/work/{WORK_ID}untilstatussettles (Intake Webhooks).
GitHub adds GITHUB_RUN_ID, GITHUB_SHA, and branch names: embed them in JSON so Trace a failing job end to end stays searchable later.
Nothing merges itself because DevOps wished it: you add secrets, you scope branch protections so forks can’t exfiltrate keys.
Quick start
- Repository secrets: Settings → Secrets and variables → Actions → New repository secrets:
AUXOT_BASE_URL:https://your-auxot-host(no trailing slash).AUXOT_API_KEY:team.…oruser.…(Pick personal or team API keys for real work).AUXOT_INTAKE_ID: id from Settings → Intakes (Trigger a workflow with an intake webhook).
- Workflow file:
.github/workflows/auxot-intake.yml(name arbitrary) withcurl+jqonubuntu-latest. - POST step: capture HTTP status: assert
202before trusting body JSON. - Poll loop: sleep + backoff; exit
1onfailedstatus so the job goes red. - Secret discipline: never
echoAPI keys; rely on GitHub secret masking; tighten Environment approvals if intake touches prod workflows (Harden your intake webhooks).
Done? Manual Run workflow from the Actions tab fires Auxot; failing polls fail the job visibly.
The agent can do that?
1. YAML review before first secret paste
Chat → Admin Agent:
Review this GitHub Actions outline for Auxot intake: POST $AUXOT_BASE_URL/v1/intake/$AUXOT_INTAKE_ID with Bearer secret, jq-parse work_id, poll GET until completed/failed. Flag mistakes: trailing slash on BASE_URL, missing -f on curl, and logging response headers that leak secrets.
Why it’s non-obvious: CI shells fail boringly: Admin Agent catches structural mistakes after you paste skeleton; you still run the workflow.
2. Green POST, stuck poll
GitHub log shows HTTP 202 + work_id but poll stays running >15m. Workflow has human approval step in Auxot. Ordered checklist: poll backoff vs real stall vs waiting on human — bullets referencing Threads/Jobs ([Trace a failing job end to end](/tutorials/trace-a-failing-job-end-to-end)).
Why it’s non-obvious: CI timeouts blame GitHub first (often human columns (Run a workflow)): paste SLA + workflow shape.
3. Reusable workflow vs duplication
Three microservices repos should trigger the same intake definition. Compare GitHub reusable workflow with `workflow_call` vs duplicating YAML — tradeoffs for secret inheritance and how widely a leaked secret would spread only.
Why it’s non-obvious: Org-level secrets vs repo secrets change rotation math: pick consciously.
Go deeper
Correlation JSON
Include minimally:
{
"github": {
"repository": "org/repo",
"run_id": "12345",
"sha": "abc…",
"ref": "refs/heads/main"
}
}
Workflow reads ${{ github.repository }}, ${{ github.run_id }}, ${{ github.sha }} into jq -n payloads: keeps Audit Logs grep-friendly.
Fork PR safety
Public repos: restrict pull_request triggers from firing intake unless you trust contributors: secrets aren’t available to forks by default; keep workflow_dispatch or internal runners for sensitive intakes.
Callbacks instead
Long polls waste minutes when workflows run 30+ minutes: configure intake callback URL and let GitHub react to an outbound webhook instead (Intake Webhooks), different architecture, smaller runner bills.
Example workflow (reference)
Single job keeps secrets in one shell: split into two steps later if you want distinct log sections.
name: Auxot intake
on:
workflow_dispatch:
jobs:
intake:
runs-on: ubuntu-latest
steps:
- name: POST intake and poll until settled
env:
AUXOT_BASE_URL: ${{ secrets.AUXOT_BASE_URL }}
AUXOT_API_KEY: ${{ secrets.AUXOT_API_KEY }}
INTAKE_ID: ${{ secrets.AUXOT_INTAKE_ID }}
run: |
set -euo pipefail
BODY=$(jq -n \
--arg repo "${{ github.repository }}" \
--arg run "${{ github.run_id }}" \
--arg sha "${{ github.sha }}" \
'{github:{repository:$repo,run_id:$run,sha:$sha}}')
RESP=$(curl -sS -w "\n%{http_code}" -X POST \
"$AUXOT_BASE_URL/v1/intake/$INTAKE_ID" \
-H "Authorization: Bearer $AUXOT_API_KEY" \
-H "Content-Type: application/json" \
-d "$BODY")
CODE=$(echo "$RESP" | tail -n1)
JSON=$(echo "$RESP" | sed '$d')
echo "$JSON"
test "$CODE" = "202"
WORK_ID=$(echo "$JSON" | jq -r '.work_id')
for i in $(seq 1 60); do
R=$(curl -sS "$AUXOT_BASE_URL/v1/intake/$INTAKE_ID/work/$WORK_ID" \
-H "Authorization: Bearer $AUXOT_API_KEY")
ST=$(echo "$R" | jq -r '.status')
echo "poll $i status=$ST"
case "$ST" in
completed) echo "$R" | jq . ; exit 0 ;;
failed) echo "$R" | jq . >&2 ; exit 1 ;;
esac
sleep 10
done
echo "timeout waiting for terminal status" >&2
exit 1
Walkthrough
Step 1: Prove intake manually
Run Trigger a workflow with an intake webhook curl from a laptop against the same INTAKE_ID: fix workflow inputs before CI noise compounds.
Step 2: Create secrets
Add AUXOT_BASE_URL, AUXOT_API_KEY, and AUXOT_INTAKE_ID at repo or environment scope.
Step 3: Commit workflow YAML
Paste the Example workflow block: it already checks 202, parses work_id, and polls until completed / failed / timeout. Split POST vs poll into separate steps if you want clearer Actions log sections (pass work_id via $GITHUB_OUTPUT).
Step 4: Run workflow_dispatch
Actions tab → select workflow → Run workflow → inspect logs: redact screenshots before Slack.
What’s next
- → Trigger a workflow with an intake webhook. Conceptual intake map when YAML feels mysterious.
- → Catch regressions after you change an agent. Before volume hits the same intake, freeze what “good” means in a pack you can re-run.
- → Harden your intake webhooks. Branch protections + rotation discipline around CI-held Bearer tokens.
- → Trace a failing job end to end. Use it when Actions goes red but Auxot shows why only in Threads/Jobs.
- → Intake Webhooks. Auth, poll paths, callbacks.
Reference
- Endpoints:
POST /v1/intake/{INTAKE_ID},GET /v1/intake/{INTAKE_ID}/work/{WORK_ID} - Manual: Intake Webhooks, Authentication & API Keys
- GitHub docs: Encrypted secrets
- See also: Self-host Auxot stage by stage, Catch regressions after you change an agent, Create a shared Team API Key, Run a workflow