Webhook Development with ngrok

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:

  1. Updates .env with webhook secrets from .config.yaml (if configured)
  2. Restarts BonsAPI to load new secrets
  3. 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 #

  1. Go to Clerk Dashboard - Webhooks
  2. Add endpoint: https://<your-ngrok-domain>/webhook/clerk
  3. Select events: user.created, user.updated, organization.created
  4. Copy signing secret to .config.yaml

Stripe #

  1. Go to Stripe Dashboard - Webhooks
  2. Add endpoint: https://<your-ngrok-domain>/webhook/stripe
  3. Select events: customer.subscription.*
  4. Copy signing secret (starts with whsec_) to .config.yaml

Testing: stripe listen --forward-to localhost:8080/webhook/stripe

Xero #

  1. Go to Xero Developer Portal
  2. Add webhook: https://<your-ngrok-domain>/webhook/xero
  3. Copy signing key to .config.yaml

QuickBooks #

  1. Go to QuickBooks Developer Portal
  2. Add webhook: https://<your-ngrok-domain>/webhook/quickbooks
  3. 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.yaml is gitignored - never commit webhook secrets
  • Static domains persist across restarts - recommended for webhook development
  • mise webhook automatically configures ngrok for Coder workspaces
  • Webhook secrets in .config.yaml override Doppler values in .env

Code References #

  • Webhook Controllers: apps/bonsapi/src/controller/webhook/
  • Webhook Services: apps/bonsapi/src/service/{provider}/webhook.rs