GC
7 min read·Updated 2026-04-13

Monitor CI with Webhooks

Custom webhook mapping turns CI failure payloads into agent prompts. Verified with curl, then wired to GitHub Actions.

What you will build

This recipe connects a CI pipeline to the bot through a custom webhook mapping. When a build fails, the CI system POSTs a JSON payload to /api/hooks/ci-failure; the mapping renders the payload into a prompt with {field.path} placeholders, runs a full agent turn on it, and returns a run ID so the caller can follow up later.

You should already understand the endpoints, tokens, and the {field.path} placeholder rules from Webhooks. This recipe does not re-explain them.

Prerequisites

  • GolemCore Bot is running on a host reachable from your CI system. For local experiments, ngrok or a tailnet is enough.
  • Webhooks are enabled in Settings → Webhooks with a bearer token set. Until this is done every webhook endpoint returns 404 Not Found.
  • The CI provider can add a custom HTTP header and body to its outbound requests — both GitHub Actions and GitLab CI can.

1. Define the custom mapping

Custom hooks are mappings stored under webhooks.mappings in the user preferences file (preferences/settings.json), but you should never edit that file by hand — use Settings → Webhooks → Custom mappings in the dashboard. Every field below maps to an input there.

name

ci-failure. The URL slug, which becomes /api/hooks/ci-failure. Must be unique. No /path/ field — the URL is always derived from this name.

Name

action

agent. A full agent turn runs on each call. Use wake instead if the CI pipeline only needs to notify and does not care about follow-up work.

wake or agent

authMode

bearer. The caller authenticates with the shared webhook token (Authorization: Bearer <token>). Switch to hmac if the CI provider signs payloads with HMAC-SHA256 — see the Variants section.

bearer or hmac

messageTemplate

The prompt the agent sees. Use {field.path} placeholders with a single pair of braces — not Mustache {{field}}. Missing keys render as <missing>, which is a useful canary in production.

Template

Fill the fields in the dashboard form and save. The mapping is live on the next request — there is no reload step and no restart.

Settings → Webhooks → New mapping
text
name:         ci-failure
action:       agent
authMode:     bearer

messageTemplate:
  CI build failed on branch {branch}.
  Commit: {commit}
  Summary: {summary}
  Logs: {log_url}

  Investigate the failure, identify the likely cause,
  and suggest a concrete fix.

Custom mappings do not pick a skill

A mapping has no skill field. If you want the agent to use a particular skill while handling CI failures, either name the skill in the messageTemplate (“Use the ci-triageskill to...”) so the agent transitions into it on intent, or make the skill description match the template wording. Skill selection always goes through skill_transition, never through webhook config.

2. Send a test payload

Before touching your CI config, simulate the payload with curl. This isolates template rendering and authentication from the CI plumbing.

Simulate a CI failure payload
bash
curl -sS -X POST http://localhost:8080/api/hooks/ci-failure \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-webhook-token" \
  -d '{
    "branch": "main",
    "commit": "9f3a1c2",
    "summary": "3 of 412 tests failing in PaymentServiceTest",
    "log_url": "https://ci.example.com/builds/1234"
  }'
Response (202 Accepted)
json
{
  "status": "accepted",
  "runId": "550e8400-e29b-41d4-a716-446655440000",
  "chatId": "hook:550e8400-e29b-41d4-a716-446655440001"
}

The agent turn runs in the background. Open Sessions and find the chatIdfrom the response — that session contains the rendered prompt as its first turn, followed by the agent's investigation. If the response is 200 OK with just {"status": "accepted", "chatId": "..."}, the mapping is set to action: wake instead of agent — fix it and try again.

3. Wire the CI system

Once the local curl works, configure your CI provider to hit the same endpoint on failure. The exact shape depends on the provider; the pattern is always the same — POST JSON whose field names match the placeholders in your messageTemplate.

GitHub Actions workflow snippet
yaml
jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/test.sh
      - name: Notify GolemCore on failure
        if: failure()
        run: |
          curl -sS -X POST ${{ secrets.GOLEMCORE_WEBHOOK_URL }} \
            -H "Content-Type: application/json" \
            -H "Authorization: Bearer ${{ secrets.GOLEMCORE_WEBHOOK_TOKEN }}" \
            -d @- <<'EOF'
          {
            "branch": "${{ github.ref_name }}",
            "commit": "${{ github.sha }}",
            "summary": "Test job failed in ${{ github.workflow }}",
            "log_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
          }
          EOF

Trigger a failing build intentionally. Within a few seconds a new session should appear in the dashboard with the rendered prompt as its first turn. If a field in the payload is missing, you will see a literal <missing> in the prompt — a useful signal that the CI side is not sending what the template expects.

Variants

Wake instead of agent

Set action: wake on the mapping. The endpoint returns 200 OK immediately and the caller never sees a runId. Use this when the CI system only wants to notify and does not track the follow-up.

Fire-and-forget

HMAC-signed payloads (GitHub, Stripe…)

Switch authMode to hmac, set hmacHeader to the provider's signature header (e.g. x-hub-signature-256), paste the shared secret into hmacSecret, and set hmacPrefix to sha256= if the provider adds one. HMAC-SHA256 is compared against the raw body in constant time.

HMAC

Block until the agent answers

Set syncResponse: true and optionally responseJsonSchema on the mapping. The HTTP caller waits for the agent to finish and gets either plain text or a schema-validated JSON body back. Useful when the CI system wants to branch on the agent's verdict.

Synchronous

Forward the result to Telegram

Set deliver: true, channel: telegram, and to: <chat id> on the mapping. The agent's answer is pushed to that channel after the run completes — the webhook call itself still returns a runId so the CI system can move on.

Cross-channel

Gotchas

401 Unauthorized

Check: the curl response body. 401 means the Authorization header is missing or the token does not match the one in Settings → Webhooks. Fix: re-check the token and the Bearer prefix. For HMAC mappings, verify the signature header name and the secret match exactly.

auth

404 Not Found on /api/hooks/ci-failure

Check: the mapping name in Settings → Webhooks. The URL slug after /api/hooks/ must match the name field exactly and is case-sensitive. Fix: rename the mapping or the curl URL — they must agree.

routing

Placeholders are not substituted

Check: the rendered prompt in the session's first turn. If it contains literal <missing> or still shows {branch} with the braces, the CI payload is missing fields the template expects — or someone used Mustache {{branch}} instead of {branch}. Fix: align payload field names with placeholders and use single-brace syntax.

template

413 Payload Too Large

Check: Settings → Webhooks → Max payload size (default 64 KB). CI logs and stack traces blow past that quickly. Fix: send just the build summary and a log URL in the payload — have the agent fetch full logs itself with a tool call.

size

What to do next