Webhook Development with ngrok #
This guide covers setting up ngrok for local webhook development. Webhooks from external services require a publicly accessible URL, which ngrok provides by tunneling traffic to your local BonsAPI (port 8080).
Quick Start #
# 1. Copy example config
cp .example.config.yaml .config.yaml
# 2. Get ngrok authtoken from https://dashboard.ngrok.com/get-started/setup
ngrok config add-authtoken <YOUR_AUTHTOKEN>
# 3. Get static domain from https://dashboard.ngrok.com/domains
# Edit .config.yaml and set your domain:
ngrok:
domain: "your-static-domain.ngrok-free.app"
# 4. Start ngrok tunnel
mise webhook
Configuration #
Local Config (.config.yaml)
#
ngrok:
domain: "your-static-domain.ngrok-free.app"
# Optional: Override webhook secrets for testing
webhook_secrets:
CLERK_WEBHOOK_SIGNING_SECRET: "whsec_your_test_secret"
STRIPE_WEBHOOK_SIGNING_SECRET: ""
XERO_WEBHOOK_SIGNING_KEY: ""
QBO_VERIFIER_TOKEN: ""
What happens when you run mise webhook:
- Updates
.envwith webhook secrets from.config.yaml(if configured) - Restarts BonsAPI to load new secrets
- Starts ngrok tunnel to
http://localhost:8080
Webhook Endpoints #
Base URL: https://<your-ngrok-domain>/webhook
| Provider | Path | Signing Secret |
|---|---|---|
| Clerk | /webhook/clerk |
CLERK_WEBHOOK_SIGNING_SECRET |
| Stripe | /webhook/stripe |
STRIPE_WEBHOOK_SIGNING_SECRET |
| Xero | /webhook/xero |
XERO_WEBHOOK_SIGNING_KEY |
| QuickBooks | /webhook/quickbooks |
QBO_VERIFIER_TOKEN |
| SharePoint | /webhook/sharepoint/{integration_id} |
N/A (validation token) |
| Google Drive | /webhook/google-drive/{integration_id} |
N/A (validation token) |
| Dropbox | /webhook/dropbox |
N/A (challenge-response) |
Provider Setup #
Clerk #
- Go to Clerk Dashboard - Webhooks
- Add endpoint:
https://<your-ngrok-domain>/webhook/clerk - Select events:
user.created,user.updated,organization.created - Copy signing secret to
.config.yaml
Stripe #
- Go to Stripe Dashboard - Webhooks
- Add endpoint:
https://<your-ngrok-domain>/webhook/stripe - Select events:
customer.subscription.* - Copy signing secret (starts with
whsec_) to.config.yaml
Testing: stripe listen --forward-to localhost:8080/webhook/stripe
Xero #
- Go to Xero Developer Portal
- Add webhook:
https://<your-ngrok-domain>/webhook/xero - Copy signing key to
.config.yaml
QuickBooks #
- Go to QuickBooks Developer Portal
- Add webhook:
https://<your-ngrok-domain>/webhook/quickbooks - Copy verifier token to
.config.yaml
SharePoint / Google Drive / Dropbox #
These use validation tokens or challenge-response, no signing secrets needed. BonsAPI handles validation automatically.
Testing #
Inspect Traffic #
Access ngrok web interface at http://localhost:4040 to view:
- Incoming requests
- Headers and body
- Response status
In Coder workspaces, access via: https://ngrok--dev--<workspace>--<user>.coder.internal.gotofu.com
View Logs #
docker logs -f bonsapi
Test Endpoints #
curl -X POST https://<your-ngrok-domain>/webhook/clerk \
-H "Content-Type: application/json" \
-d '{"type":"user.created","data":{}}'
Troubleshooting #
Signature Verification Failures #
- Verify signing secret is correct (no extra spaces)
- Check BonsAPI restarted after updating secrets:
docker logs bonsapi - Inspect request in ngrok dashboard (http://localhost:4040)
ngrok Session Expires #
- Use static domains (recommended)
- Upgrade to ngrok paid plan for persistent sessions
Integration Webhooks (SharePoint/Google Drive) 404 #
- Verify
{integration_id}is valid - Check integration exists in database
Notes #
.config.yamlis gitignored - never commit webhook secrets- Static domains persist across restarts - recommended for webhook development
mise webhookautomatically configures ngrok for Coder workspaces- Webhook secrets in
.config.yamloverride Doppler values in.env
Code References #
- Webhook Controllers:
apps/bonsapi/src/controller/webhook/ - Webhook Services:
apps/bonsapi/src/service/{provider}/webhook.rs