SET UP WEBHOOKS
Prerequisites: Swamp installed, a repository with a workflow, familiarity
with swamp serve. See swamp serve how-to guides
for server setup.
Start the server with a GitHub webhook
The --webhook flag registers an HTTP endpoint that triggers a workflow when it
receives a POST request. The format is
<route>:<workflow>:<secret>[:<scheme>[:<header>[:<prefix>]]].
swamp serve --webhook '/hooks/github:deploy-pipeline:mysecret'The server validates the request signature using the X-Hub-Signature-256
header (HMAC-SHA256) before triggering the workflow.
Test locally with curl
Compute the HMAC signature and send a test request:
SECRET="mysecret"
BODY='{"ref":"refs/heads/main"}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print "sha256="$2}')
curl -X POST http://localhost:9090/hooks/github \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: $SIG" \
-d "$BODY"Use an environment variable for the secret
To avoid putting secrets in command arguments:
swamp serve --webhook '/hooks/github:deploy-pipeline:@env=WEBHOOK_SECRET'Use a file-based secret
swamp serve --webhook '/hooks/github:deploy-pipeline:@file=/run/secrets/webhook'Use a vault-managed secret
Store the secret in a vault, then reference it with @vault=<name>:<key>:
swamp serve --webhook '/hooks/github:deploy-pipeline:@vault=prod-secrets:webhook-key'Any configured vault backend works. See the vaults reference for vault setup and the secret source explanation for when to choose vaults over environment variables or files.
Set up a Linear webhook
Use the linear scheme. Linear sends a signature in the Linear-Signature
header:
swamp serve --webhook '/hooks/linear:triage-workflow:@env=LINEAR_SECRET:linear'Set up a Stripe webhook
Use the stripe scheme. Stripe uses the Stripe-Signature header with
timestamp-based signatures:
swamp serve --webhook '/hooks/stripe:billing-workflow:@env=STRIPE_WEBHOOK_SECRET:stripe'Set up a Jira webhook
Use the jira scheme. Jira sends a signature in the X-Hub-Signature header
with a sha256= prefix:
swamp serve --webhook '/hooks/jira:on-issue:@env=JIRA_WEBHOOK_SECRET:jira'Set up a generic webhook
For services that send a static token in a custom header, use the generic
scheme. Specify the header name and optional value prefix:
swamp serve --webhook '/hooks/custom:my-workflow:mytoken:generic:X-Api-Key'If the service sends the token with a prefix (e.g., Bearer mytoken):
swamp serve --webhook '/hooks/custom:my-workflow:mytoken:generic:Authorization:Bearer 'Access webhook payload in workflow CEL
Inside a workflow step, the webhook payload is available through CEL expressions:
webhook.headers— request headerswebhook.body— parsed JSON bodywebhook.params— URL query parameters
Correlate webhook-triggered runs with your traces
Webhook requests automatically propagate W3C traceparent and tracestate
headers into the triggered workflow execution. Include these headers in your
webhook request to make the swamp run appear as a child span of your trace:
curl -X POST http://localhost:9090/hooks/github \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: $SIG" \
-H "traceparent: 00-abcd1234abcd1234abcd1234abcd1234-abcd1234abcd1234-01" \
-d "$BODY"No additional configuration is needed on the swamp serve side.
Receive Telegram bot updates
Use the @swamp/telegram extension scheme to receive Telegram bot updates.
Telegram uses a static secret token in the X-Telegram-Bot-Api-Secret-Token
header — weaker than HMAC, so TLS is required.
1. Configure serve.yaml
webhooks:
- route: /hooks/telegram
workflow: on-telegram
secret: "@env=TELEGRAM_BOT_SECRET"
scheme: "@swamp/telegram"2. Register the webhook with Telegram
curl -X POST "https://api.telegram.org/bot$BOT_TOKEN/setWebhook" \
-d "url=https://swamp.example.com/hooks/telegram" \
-d "secret_token=$TELEGRAM_BOT_SECRET"The secret_token must match the secret configured in serve.yaml. Telegram
sends it back in every update request for the server to verify.
3. Access the update in your workflow
Use CEL expressions to read the Telegram update payload:
steps:
- name: handle-message
task:
type: model_method
modelIdOrName: telegram-handler
methodName: process
inputs:
chatId: "${{ webhook.body.message.chat.id }}"
text: "${{ webhook.body.message.text }}"Security caveats
- The secret is transmitted in the clear as a header value, not derived from the request body. Always serve over HTTPS.
- The handler uses constant-time comparison to prevent timing attacks.
- Do not use
@swamp/telegramover plain HTTP — any intermediary can read the secret.
Use an extension webhook scheme
Extension webhook schemes (@collective/name) provide custom verification logic
for services not covered by the built-in schemes. Extensions are auto-pulled
from trusted collectives on first reference.
webhooks:
- route: /hooks/custom
workflow: on-custom-event
secret: "@env=CUSTOM_SECRET"
scheme: "@acme/my-webhook"
config:
challengeField: challengeThe config object is passed to the extension handler. See the
webhooks reference
for the handler lifecycle and the
manifest reference for how to
author a webhook extension.
For the full --webhook flag specification, run swamp help serve.