Blog · Guide · 29 October 2026

Trigger an AEGIS workflow from a signed webhook

Also in: Español, Português, 日本語, Deutsch, Français, 한국어, 简体中文, Italiano, हिन्दी

Disclosure: 100monkeys.ai builds AEGIS, so read this guide as a maker's account. It was drafted by one of our AI agents from the public AEGIS documentation, Configuring Webhooks, and reviewed by the company. Every command and code block below is copied from that page; none was run for this guide. Where the docs leave a question open, the last section says so.

AEGIS by 100monkeys.ai, in beta, can start a workflow when another system sends it an event: a push on GitHub, a CI job finishing, any service that can make an HTTP request. The sender needs no account on your node. In the docs' words, "Authentication is handled by HMAC-SHA256 signature verification, not OAuth." This guide sets up one source end to end, from the shared secret to a tested request, and covers the errors you will meet on the way.

Step 1. Name the source

Every webhook arrives at POST /v1/webhooks/{source}. The source is "a lowercase slug that identifies the system sending the event", such as github, stripe or my-ci. Pick one per sending system: the secret, the route and the duplicate check all key on it.

Step 2. Set the shared secret on the orchestrator

Secrets are scoped per tenant and source, so two tenants that both call their source github hold "completely independent secrets". Each pair is one environment variable on the orchestrator, named by this pattern:

# Pattern: AEGIS_WEBHOOK_SECRET_<UPPER_TENANT>_<UPPER_SOURCE>
# Hyphens and dots in tenant id and source name are replaced with underscores

export AEGIS_WEBHOOK_SECRET_U_ABC123_GITHUB="your-shared-secret-here"
export AEGIS_WEBHOOK_SECRET_U_ABC123_STRIPE="another-secret"
export AEGIS_WEBHOOK_SECRET_U_DEF456_MY_CI="third-secret"

So tenant u-abc123 with source my-ci reads AEGIS_WEBHOOK_SECRET_U_ABC123_MY_CI. If the variable is missing for the resolved pair, the request is rejected with 401 secret_not_found. Give the same secret to the sender and to nobody else.

Step 3. Route the source to a workflow

A direct route maps the source to one workflow, so routing is "instant and deterministic". In the current release, direct routes are "configured via orchestrator workflow routing configuration", and the page notes "there is no aegis stimulus routes CLI command yet". Without a direct route, the pipeline falls back to a configured RouterAgent, which classifies the event with a language model. With neither, the request fails with 422 no_router_configured. For a single, known sender, a direct route is the one to want: the same event always starts the same workflow.

Step 4. Sign the raw body on the sender

The sender computes an HMAC-SHA256 of the raw request body with the shared secret and sends it in X-Aegis-Signature with the prefix sha256=. The docs' Python example:

import hashlib
import hmac
import json
import requests

def sign_payload(body: bytes, secret: str) -> str:
    sig = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return f"sha256={sig}"

payload = json.dumps({"event": "push", "ref": "refs/heads/main"}).encode()
signature = sign_payload(payload, WEBHOOK_SECRET)

requests.post(
    "https://your-aegis-node/v1/webhooks/github",
    data=payload,
    headers={
        "Content-Type": "application/json",
        "X-Aegis-Signature": signature,
    },
)

The page also gives the same in Node.js. The one rule it puts in bold: compute the HMAC over the raw request body bytes before any parsing, because "Computing over a re-serialized object will produce a different signature." Sign the exact bytes you send.

Step 5. Test the endpoint with curl

# Generate a test signature
SECRET="your-shared-secret-here"
BODY='{"event":"push","ref":"refs/heads/main"}'
SIG="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"

# Send the test webhook
curl -X POST https://your-aegis-node/v1/webhooks/github \
  -H "Content-Type: application/json" \
  -H "X-Aegis-Signature: $SIG" \
  -d "$BODY"

A success answers with two ids:

{
  "stimulus_id": "stim-uuid",
  "workflow_execution_id": "wfex-uuid"
}

The second is the workflow run the event started; follow it as you would any other run.

Step 6. Stop duplicate deliveries

Senders retry. Forward the provider's delivery id as X-Idempotency-Key: for GitHub that is X-GitHub-Delivery, and for your own sender "Any stable per-event UUID". The orchestrator deduplicates on source and key for 24 hours, and a repeat answers 409:

{
  "error": "idempotent_duplicate",
  "original_stimulus_id": "stim-original-uuid"
}

Step 7. Read the error you get

The page's error table, in the order you are likely to meet it:

Step 8. Use the authenticated endpoint for internal callers

A service that already holds a Keycloak account can skip the shared secret and post to POST /v1/stimuli with a bearer token instead, giving source, content and an idempotency_key in the body. Keep the webhook route for systems you do not run.

What the docs leave open

Next

The workflow a webhook starts is a state machine you write. Our guide AEGIS workflow states and transitions, step by step builds one. AEGIS installs with the command printed on 100monkeys.ai:

curl -fsSL https://get.100monkeys.ai | bash

Install AEGIS from 100monkeys.ai