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}: Bearer user. or team. (Intake Webhooks).
  • Poll: GET /v1/intake/{INTAKE_ID}/work/{WORK_ID} until status settles (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

  1. Repository secrets: Settings → Secrets and variables → Actions → New repository secrets:
  2. Workflow file: .github/workflows/auxot-intake.yml (name arbitrary) with curl + jq on ubuntu-latest.
  3. POST step: capture HTTP status: assert 202 before trusting body JSON.
  4. Poll loop: sleep + backoff; exit 1 on failed status so the job goes red.
  5. Secret discipline: never echo API 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

Reference