Smart WebhooksHMAC Verification

HMAC Signature Verification

Smart Webhooks support HMAC-SHA256 signature verification to ensure incoming requests are authentic. When a signing_secret is configured on a webhook, the execution endpoint verifies the request signature before processing.

How It Works

  1. The webhook source (GitHub, Stripe, etc.) signs the request body using a shared secret
  2. The signature is sent in a request header
  3. Aerostack verifies the signature against the shared secret
  4. If the signature matches, the webhook is processed. Otherwise, it is rejected.

Setting a Signing Secret

Set the signing_secret when creating or updating a Smart Webhook:

# On creation
curl -X POST https://api.aerostack.dev/api/smart-webhooks \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "github-webhook",
    "workspace_id": "ws_abc123",
    "instructions": "Process GitHub events...",
    "source_type": "github",
    "signing_secret": "your_shared_secret_here"
  }'
# Update an existing webhook
curl -X PATCH https://api.aerostack.dev/api/smart-webhooks/swh_your_id \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "signing_secret": "your_new_secret_here"
  }'

The signing secret can be up to 256 characters long.

Supported Signature Headers

The execution endpoint checks for the signature in these headers (in order of priority):

HeaderUsed By
X-Hub-Signature-256GitHub
X-Signature-256Generic
X-Webhook-SignatureGeneric / custom services

The first header found is used for verification. If none of these headers are present and a signing secret is configured, the request is silently rejected.

Signature Format

The expected signature format is:

sha256=<hex_digest>

Where <hex_digest> is the HMAC-SHA256 hash of the raw request body using the signing secret as the key, encoded as lowercase hexadecimal.

If the signature header value does not start with sha256=, the prefix is automatically added before comparison.

Provider-Specific Setup

GitHub

  1. In your GitHub repo, go to Settings > Webhooks > Edit (or Add webhook)
  2. Set the Secret field to the same value as your Smart Webhook’s signing_secret
  3. GitHub will automatically send the X-Hub-Signature-256 header with each delivery
# Create webhook with GitHub secret
curl -X POST https://api.aerostack.dev/api/smart-webhooks \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "github-events",
    "workspace_id": "ws_abc123",
    "instructions": "Handle GitHub events...",
    "source_type": "github",
    "signing_secret": "whsec_your_github_secret"
  }'

Stripe

Stripe uses a different signature format (Stripe-Signature header with t= timestamp and v1= signature). Currently, Smart Webhooks check for X-Hub-Signature-256, X-Signature-256, or X-Webhook-Signature headers only.

⚠️

Stripe’s native Stripe-Signature header is not currently supported for automatic verification. To use Stripe webhooks with HMAC verification, you can set up a proxy that converts the Stripe signature to the X-Webhook-Signature format, or omit the signing_secret and rely on Stripe’s IP allowlisting instead.

Custom Services

For your own services, compute the HMAC-SHA256 signature and send it in any of the supported headers:

# Compute signature and send webhook
SECRET="your_shared_secret"
BODY='{"event":"user.created","data":{"id":"usr_123"}}'
SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)
 
curl -X POST https://api.aerostack.dev/api/webhooks/smart/my-webhook \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Signature: sha256=$SIGNATURE" \
  -d "$BODY"

Security Notes

No Signing Secret

If signing_secret is not set on a webhook, all incoming requests are accepted without signature verification.

⚠️

Running without a signing secret is not recommended for production use. Without signature verification, anyone who discovers your webhook URL can send arbitrary payloads that will be processed by the AI agent and trigger tool calls in your workspace.

Verifying Your Setup

Since all webhook responses return { ok: true } regardless of outcome, use these methods to verify your setup:

  1. Check run history — After sending a test webhook, check if a run was recorded:

    curl https://api.aerostack.dev/api/smart-webhooks/swh_your_id/runs \
      -H "Authorization: Bearer YOUR_JWT_TOKEN"

    If the run appears, the signature was valid.

  2. GitHub delivery log — GitHub shows delivery status in the webhook settings page. A 200 response means Aerostack received it, but check the runs endpoint to confirm it was actually processed.

  3. Send a test with a known-good signature — Use the curl example from the “Custom Services” section above with your actual signing secret.