Webhook Triggers
Webhook trigger mode lets external services (GitHub, Stripe, Shopify, Linear, Zapier) push events directly to your Wrapd endpoint. The full request body is piped to your command’s stdin as JSON.
Configuration
Section titled “Configuration”endpoints: - name: github-deploy command: ./deploy.sh trigger: webhook webhook_source: github # github, stripe, shopify, or generic webhook_events: [push] # filter by event type (optional) secret: $wrapd:GITHUB_SECRET # HMAC verification secret public: true # most webhooks can't send API keys timeout: 60| Field | Default | Description |
|---|---|---|
trigger | http | Set to webhook for webhook mode |
webhook_source | generic | Provider type: github, stripe, shopify, or generic |
webhook_events | [] | Event type filter (provider-specific). Empty = accept all |
secret | — | Secret reference for HMAC verification |
async | true | Return 202 immediately (true) or wait for output (false) |
Webhook sources
Section titled “Webhook sources”Wrapd verifies provider webhook signatures automatically — you don’t need to implement HMAC verification yourself.
| Source | Verification | Event header | Signature format |
|---|---|---|---|
github | HMAC-SHA256 | X-GitHub-Event | sha256=<hex> |
stripe | HMAC-SHA256 + replay protection | type in body | t=<ts>,v1=<hex> |
shopify | HMAC-SHA256 | X-Shopify-Topic | Base64 |
generic | HMAC-SHA256 (agent-side) | — | sha256=<hex> |
GitHub
Section titled “GitHub”Wrapd checks the X-Hub-Signature-256 header against your secret. Events are filtered by X-GitHub-Event header.
- name: on-push command: ./deploy.sh trigger: webhook webhook_source: github webhook_events: [push, pull_request] secret: $wrapd:GITHUB_SECRET public: trueSetup in GitHub:
- Go to repo Settings → Webhooks → Add webhook
- Payload URL:
https://api.wrapd.sh/v1/yourname/on-push - Content type:
application/json - Secret: same value as your managed secret
- Events: select the events you want
Stripe
Section titled “Stripe”Wrapd parses the Stripe-Signature header, verifies the HMAC, and enforces replay protection (rejects events older than 5 minutes). Events are filtered by the type field in the request body.
- name: payment-received command: ./fulfill-order.sh trigger: webhook webhook_source: stripe webhook_events: [checkout.session.completed, payment_intent.succeeded] secret: $wrapd:STRIPE_WEBHOOK_SECRET public: trueSetup in Stripe:
- Go to Developers → Webhooks → Add endpoint
- Endpoint URL:
https://api.wrapd.sh/v1/yourname/payment-received - Select events to listen for
- Copy the signing secret to your Wrapd managed secrets
Shopify
Section titled “Shopify”Wrapd checks the X-Shopify-Hmac-Sha256 header (Base64-encoded HMAC). Events are filtered by X-Shopify-Topic header.
- name: new-order command: ./notify-team.sh trigger: webhook webhook_source: shopify webhook_events: [orders/create, orders/paid] secret: $wrapd:SHOPIFY_SECRET public: trueSetup in Shopify:
- Go to Settings → Notifications → Webhooks
- Add webhook with your endpoint URL
- Copy the HMAC shared secret to your Wrapd managed secrets
Generic
Section titled “Generic”The default mode. HMAC verification happens on the agent side using X-Hub-Signature-256 or X-Signature-256 headers. Use this for any webhook provider not explicitly supported.
- name: on-event command: ./handle.sh trigger: webhook secret: $wrapd:MY_SECRET public: trueEvent filtering
Section titled “Event filtering”When webhook_events is set, Wrapd checks the incoming event type and silently ignores non-matching events (returns 200 OK so the provider doesn’t retry).
This lets you have multiple endpoints listening to the same webhook source but handling different events:
- name: deploy-on-push command: ./deploy.sh trigger: webhook webhook_source: github webhook_events: [push] secret: $wrapd:GITHUB_SECRET public: true
- name: pr-check command: ./run-tests.sh trigger: webhook webhook_source: github webhook_events: [pull_request] secret: $wrapd:GITHUB_SECRET public: trueHow it works
Section titled “How it works”- An external service sends a POST to your endpoint URL
- The API verifies the provider signature (for non-generic sources)
- The API checks if the event type matches the filter
- The request body is forwarded to the agent
- The agent pipes the body to your command’s stdin
- Response depends on
asyncmode
Async vs sync
Section titled “Async vs sync”Async (default) — returns HTTP 202 immediately:
{"status": "accepted", "request_id": "..."}Use this for GitHub, Stripe, Shopify, and most webhook senders that expect a fast response.
Sync (async: false) — waits for the command to finish and returns stdout:
HTTP 200<command stdout>Use this for Slack slash commands or services that read the response body.
Payload delivery
Section titled “Payload delivery”Your command receives the full request body on stdin:
#!/bin/bash# deploy.sh — receives GitHub push payload on stdinPAYLOAD=$(cat)REF=$(echo "$PAYLOAD" | jq -r '.ref')REPO=$(echo "$PAYLOAD" | jq -r '.repository.full_name')
echo "Deploying $REPO ($REF)..."git pull && make buildThe agent also injects convenience environment variables:
| Variable | Value |
|---|---|
WRAPD_WEBHOOK | 1 |
WRAPD_ENDPOINT | Endpoint name (e.g. on-push) |
WRAPD_REQUEST_ID | Unique request UUID |
Secret management
Section titled “Secret management”Webhook secrets should be stored as managed secrets (not environment variables) for API-side verification:
# In the dashboard: Secrets → Add secret# Name: GITHUB_SECRET# Value: your-github-webhook-secretThen reference it in your endpoint config:
secret: $wrapd:GITHUB_SECRETFor generic source, you can also use local environment variables ($ENV_VAR) since verification happens on the agent side.