Skip to content

Webhooks

Integrate external services with Moxxy agents via HMAC-signed inbound webhooks.

Overview

┌─────────────┐    POST /v1/hooks/{token}    ┌─────────────┐     ┌─────────────┐
│  External   │  ─────────────────────────▶  │   Moxxy     │ ──▶ │   Agent     │
│  Service    │   X-Signature: sha256=...    │   Gateway   │     │   Run       │
└─────────────┘                              └─────────────┘     └─────────────┘

Webhooks allow external services (GitHub, monitoring tools, CI/CD pipelines, custom applications) to trigger agent runs by sending HTTP POST requests to a unique URL. Payloads are verified via HMAC-SHA256 signatures.

Registering a Webhook

Webhooks are registered by the agent itself using the webhook.register primitive during a run:

webhook.register("github-push")

The primitive returns:

  • token -- Unique URL path component for /v1/hooks/{token}
  • secret -- HMAC secret for the external service to sign payloads

Configure the external service to send payloads to http://your-server:3000/v1/hooks/{token} with the HMAC signature in the X-Signature header.

Receiving Webhooks

POST /v1/hooks/{token}

This endpoint does not require Bearer token authentication. Instead, the request body is verified via HMAC signature.

Headers

HeaderRequiredDescription
X-SignatureYessha256={hex_digest} of the request body
Content-TypeYesapplication/json

Example

bash
BODY='{"event":"push","ref":"refs/heads/main","repo":"user/project"}'
SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk '{print $2}')

curl -X POST http://127.0.0.1:3000/v1/hooks/whk_abc123 \
  -H "Content-Type: application/json" \
  -H "X-Signature: sha256=$SIGNATURE" \
  -d "$BODY"

Response (200)

json
{
  "status": "accepted",
  "delivery_id": "del_xyz789"
}

When accepted, Moxxy starts a new run for the associated agent with the webhook payload as context.

Managing Webhooks

List Webhooks for an Agent

GET /v1/agents/{name}/webhooks
bash
curl http://127.0.0.1:3000/v1/agents/devops/webhooks
json
[
  {
    "slug": "github-push",
    "token": "whk_abc123",
    "created_at": "2025-03-15T10:00:00Z",
    "last_delivery": "2025-03-15T12:00:00Z",
    "delivery_count": 42
  }
]

Delete a Webhook

DELETE /v1/agents/{name}/webhooks/{slug}
bash
curl -X DELETE http://127.0.0.1:3000/v1/agents/devops/webhooks/github-push

The webhook token is immediately invalidated. Subsequent requests to the hook URL return 404.

Token Rotation

Rotate a webhook's token and HMAC secret using the webhook.rotate primitive:

webhook.rotate("github-push")

This generates a new token and secret. The old token is invalidated immediately. Update the external service with the new URL and secret.

HMAC Signature Verification

External services must sign the raw request body using HMAC-SHA256:

  1. Compute HMAC-SHA256(secret, raw_request_body)
  2. Hex-encode the digest
  3. Send as X-Signature: sha256={hex_digest}

Moxxy recomputes the HMAC server-side and compares. Mismatches result in a 401 response.

Signing Examples

Node.js:

javascript
const crypto = require('crypto');

function signPayload(secret, body) {
  return 'sha256=' + crypto.createHmac('sha256', secret)
    .update(body)
    .digest('hex');
}

Python:

python
import hmac
import hashlib

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

Go:

go
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func signPayload(secret string, body []byte) string {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    return fmt.Sprintf("sha256=%s", hex.EncodeToString(mac.Sum(nil)))
}

Delivery Tracking

All webhook deliveries are recorded in the webhook_deliveries table, providing a complete audit trail.

FieldDescription
delivery_idUnique delivery identifier
webhook_slugThe webhook that received the delivery
source_ipIP address of the sender
signature_validWhether the HMAC signature passed verification
run_idRun ID created to process the payload (if accepted)
status_codeHTTP status code returned to the sender
received_atTimestamp of the delivery

Deliveries with invalid signatures are still recorded (with signature_valid: false) for security monitoring. These requests are rejected with 401 but the attempt is logged.

Use Cases

GitHub Push Notifications

Configure a GitHub webhook to notify your devops agent of pushes:

  1. Agent registers webhook: webhook.register("github-push")
  2. Configure in GitHub: Repository > Settings > Webhooks
    • URL: https://your-server/v1/hooks/{token}
    • Secret: the HMAC secret from registration
    • Content type: application/json
    • Events: select "Just the push event"

Monitoring Alerts

Route alerts from Prometheus AlertManager, PagerDuty, or other monitoring tools to an ops agent for automated triage.

CI/CD Pipeline Events

Notify agents of build completions, deployment statuses, or test results from Jenkins, GitLab CI, or GitHub Actions.

Security Best Practices

  1. Always verify signatures -- Never disable HMAC verification
  2. Use HTTPS in production -- Encrypt webhook traffic in transit
  3. Rotate secrets periodically -- Use webhook.rotate to cycle credentials
  4. Monitor delivery logs -- Watch for failed signature validations, which may indicate tampering
  5. Review security events -- security.violation events in the SSE stream indicate blocked attempts

Open source · Self-hosted · Data sovereign