Back to Home

GitHub Actions integration

Page the on-call responder when a GitHub Actions workflow fails. Add a step to your workflow that POSTs a small JSON body to a Unit Oncall Webhook URL on failure. Unit Oncall normalizes it, routes it to a Schedule, and notifies on-call.

Setup

  1. In Unit Oncall, create a Webhook (Integrations → Webhooks → New). Choose a Team and default Schedule. Either auth mode works because a workflow step can send both a body and headers. Standard mode keeps the URL self-contained; Secure mode lets you store the secret separately. Copy the generated Webhook URL.

  2. Store the Webhook URL (and, for Secure mode, the secret) as repository or organization secrets — for example UNIT_ONCALL_WEBHOOK_URL and UNIT_ONCALL_WEBHOOK_SECRET.

  3. Add a failure-triggered step to your workflow:

    - name: Notify Unit Oncall on failure
      if: failure()
      env:
        UNIT_ONCALL_WEBHOOK_URL: ${{ secrets.UNIT_ONCALL_WEBHOOK_URL }}
        RUN_ID: ${{ github.run_id }}
        WORKFLOW: ${{ github.workflow }}
        REPOSITORY: ${{ github.repository }}
        REF: ${{ github.ref_name }}
        ACTOR: ${{ github.actor }}
        RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
      run: |
        # Build the JSON with jq (preinstalled on GitHub-hosted runners) so values
        # containing quotes or newlines can't break it. Context values are passed via
        # env, not interpolated into the body, to avoid shell / JSON injection.
        jq -n \
          --arg run_id "$RUN_ID" \
          --arg workflow "$WORKFLOW" \
          --arg repository "$REPOSITORY" \
          --arg ref "$REF" \
          --arg actor "$ACTOR" \
          --arg run_url "$RUN_URL" \
          '{run_id: $run_id, workflow: $workflow, repository: $repository, ref: $ref, actor: $actor, run_url: $run_url}' \
          | curl --fail --silent --show-error -X POST "$UNIT_ONCALL_WEBHOOK_URL" \
              -H "Content-Type: application/json" -d @-
    

    For Secure mode, also pass the secret via env (e.g. WEBHOOK_SECRET: ${{ secrets.UNIT_ONCALL_WEBHOOK_SECRET }}) and add -H "X-Webhook-Secret: $WEBHOOK_SECRET" to the curl command.

Webhook URL

Unit Oncall exposes a single unified receive endpoint. The token in the path identifies the webhook (and, in Standard mode, carries the secret):

POST https://<your-api-host>/api/v1/wh/{token}
  • {token} is an opaque, encrypted value generated when you create the Webhook. Store it in a GitHub secret, not in plaintext in the workflow file.
  • Standard auth mode: the secret is embedded in the token, so the URL is self-contained.
  • Secure auth mode: send the secret as the X-Webhook-Secret header (shown above) or a webhook_secret body field.

Recommended transformation rule

Because the payload above is your own custom shape, the rule maps your chosen fields directly. It uses run_id as the fingerprint (each failed run is a distinct alert), builds a title from the workflow and repository, and sets a static critical severity since the step only fires on failure.

{
  "name": "GitHub Actions failures",
  "description": "Maps a custom GitHub Actions failure payload to Unit Oncall alerts.",
  "is_active": true,
  "mappings": {
    "fingerprint": {
      "type": "jsonpath",
      "path": "$.run_id"
    },
    "title": {
      "type": "format",
      "format": "CI failed: {$.workflow} ({$.repository})"
    },
    "severity": {
      "type": "static",
      "value": "critical"
    },
    "message": {
      "type": "format",
      "format": "Workflow {$.workflow} on {$.ref} failed. Triggered by {$.actor}. Details: {$.run_url}"
    },
    "tags": {
      "type": "jsonpath",
      "path": "$.repository"
    }
  }
}

See the Transformation Rule reference for the full field and mapping syntax.

Troubleshooting

  • No alert arrives. Confirm the step actually ran — if: failure() only fires when an earlier step failed. Check the step logs for the curl exit code; --fail makes curl return non-zero on a 4xx/5xx so the workflow surfaces delivery errors.
  • 403 webhook disabled. The Webhook's is_enabled is false. Enable it.
  • 400 bad request. The token is malformed (often a truncated secret) or, in Secure mode, the X-Webhook-Secret is missing or wrong.
  • Every run reuses one alert. run_id is unique per run, so each failure is a separate alert. If you instead want to group by workflow, point fingerprint at $.workflow (note: a later success will not auto-resolve it, since this payload has no resolved signal).
  • Want success/recovery signals too? This integration only fires on failure. To resolve alerts automatically you would also send a "resolved" notification on success with the same fingerprint.