Test and Debug Webhooks on Your Localhost

View as Markdown

Testing webhook integrations during local development has traditionally been challenging. You need a public URL to receive webhooks, but your application is running on localhost.

The Hookdeck CLI solves this by establishing a secure connection from your local development environment to Hookdeck, allowing you to receive, inspect, and debug webhooks in real-time without deploying your code or exposing your local environment to the internet. Built for team workflows, the CLI enables multiple developers to work independently on the same webhook sources while filtering events specific to their feature work.

Skip to the Quickstart ->

Read the quickstart guide to start testing webhooks locally in minutes.

This guide explains how to use the Hookdeck CLI to test and debug webhook integrations during local development. It assumes you're building an application that consumes webhooks from third-party providers (like Shopify, Stripe, Twilio) and want to test your webhook handlers before deploying.

Why Test Webhooks Locally?

Developing webhook integrations without local testing capabilities creates significant friction in your development workflow:

  • Slow feedback loops: Deploy to staging/production just to test changes
  • Limited debugging: No access to debuggers or local logs for webhook processing
  • Complex setup: Managing ngrok tunnels or other forwarding solutions
  • Production risk: Testing in production environments with real data
  • Collaboration challenges: Multiple developers sharing webhook URLs

The Hookdeck CLI eliminates these issues by bringing production-like webhook delivery directly to your local machine.

Key Benefits

No Exposure of Local Environment

Your local application stays behind your firewall with no ports opened or tunnels exposed. Hookdeck provides the public webhook URL (Source URL) that providers send to, while the CLI establishes a secure, authenticated outbound connection that forwards only the webhooks you've configured to your localhost.

Real-Time Request Inspection

See every webhook request as it arrives, with full headers, body, and metadata displayed in your terminal with syntax highlighting.

Instant Event Retry

Captured a webhook? Retry it instantly to your local server with a single keystroke or click of a button. No need to trigger the event again from the source.

Production Parity

Receive webhooks exactly as they'll be delivered in production, including signature verification, retry behavior, and rate limiting.

Multiple Connection Support

Route different webhook sources to different local ports or paths, testing multiple integrations simultaneously.

Team Collaboration

Multiple developers can work on webhook integrations simultaneously without conflicts. Each developer runs their own CLI instance connected to shared webhook sources, with session-level filtering to receive only the events relevant to their current feature work. The shared dashboard provides team-wide visibility for debugging and knowledge sharing.

How the CLI Works

The Hookdeck CLI establishes a persistent connection to Hookdeck and listens for events from specified Source . When you run hookdeck listen, the CLI automatically creates (or reuses) a Connection between your Source and a CLI-type Destination . This Connection routes webhook events through the CLI to your local development server.

Third-Party Provider → Hookdeck Source → Connection → CLI Destination → localhost:3000/webhooks

What happens when a webhook arrives:

  1. A webhook provider sends a request to your Hookdeck Source URL
  2. Hookdeck receives the request and creates an event
  3. The event is routed through the Connection to the CLI Destination
  4. The CLI forwards the event to your local server (e.g., http://localhost:3000/webhooks)
  5. Your local server processes the webhook and returns a response
  6. The CLI sends the response back to Hookdeck for logging

All webhook data, including the request and your response, is logged in your Hookdeck dashboard. You can view requests (inbound webhooks from providers) and events (outbound deliveries to your destinations) for inspection, debugging, and retry.

Testing Webhooks Locally

Setting Up Your Test Environment

Start the CLI to receive webhooks on your local machine:

# Listen to a specific source on port 3000
hookdeck listen 3000 stripe-source

# Listen to multiple sources
hookdeck listen 3000 stripe-source,shopify-source

# Listen to all sources
hookdeck listen 3000 '*'

Organize endpoints by path: Configure different paths for different webhook providers. Set your Stripe Connection's CLI Destination path to /webhooks/stripe and your Shopify Connection's path to /webhooks/shopify. When you run hookdeck listen, the CLI automatically routes each source to its configured endpoint.

Alternatively, use the --path flag for quick testing:

hookdeck listen 3000 stripe-source --path /webhooks/stripe

Infrastructure as Code: Use the CLI, Hookdeck API, or Terraform Provider to script your webhook configuration, ensuring dev/staging/prod environments mirror each other.

Testing New Webhook Handlers

Develop and test webhook processing logic locally with a fast feedback loop:

Rapid iteration with retry: Capture a webhook once, then retry it repeatedly while iterating on your handler code:

  1. Receive a webhook from your provider (or send a test webhook)
  2. Press r in the CLI to retry the event to your local server
  3. Modify your code and press r again to test immediately
  4. No need to trigger new webhooks from your provider for each code change

Test signature verification: The CLI forwards webhooks with authentic provider signatures, allowing you to verify your signature validation code works before deploying:

// Your signature verification code
const signature = req.headers['x-stripe-signature'];
const isValid = verifyStripeSignature(req.body, signature, secret);
if (!isValid) {
  return res.status(401).json({ error: 'Invalid signature' });
}

You have two options for signature verification:

  1. Verify provider signatures directly: The original provider headers and body are preserved, allowing you to implement and test provider-specific signature verification (e.g., Stripe's stripe-signature header) in your local code.

  2. Verify Hookdeck signatures: Hookdeck can authenticate incoming requests from providers for you. You can then simplify your implementation by verifying only the Hookdeck signature, centralizing signature verification logic across all your webhook providers.

Both approaches work during local development, allowing you to choose the verification strategy that best fits your architecture.

Test integration flows: Verify the complete flow from webhook receipt through your business logic, database updates, and any outbound API calls your handlers make.

Testing Error Handling

Modify your handler to return different status codes and verify your application behaves correctly:

// Test 500 error handling
app.post('/webhook', (req, res) => {
  res.status(500).json({ error: 'Simulated failure' });
});

Then retry the event (press r in the CLI) to verify your application logs errors properly, cleans up resources, and handles retries as expected.

Testing Edge Cases and Idempotency

Test your webhook handlers against various scenarios:

Test idempotency: Retry the same event multiple times using the CLI's r key to verify your handler correctly handles duplicate webhooks. Your application should process the webhook once and safely ignore subsequent retries.

Test with varied webhook data: Use your provider's test/sandbox mode to trigger webhooks with different data (various amounts, statuses, event types). Capture these diverse webhooks and retry them to test different code paths.

Simulate validation errors: Modify your handler code temporarily to test how it handles edge cases:

// Simulate missing required field
if (!req.body.amount) {
  return res.status(400).json({ error: 'Missing required field: amount' });
}

// Simulate unexpected data type
if (typeof req.body.amount !== 'number') {
  return res.status(400).json({ error: 'Invalid amount type' });
}

Test rate limiting: Send multiple webhooks in quick succession to verify your application handles high-volume scenarios correctly.

Testing Retry Behavior

Configure retry rules in your Connection to test how your application handles failed webhook deliveries:

  1. Configure retry rules in your Connection (via dashboard or infrastructure-as-code)
  2. Modify your handler to return a 500 error
  3. Trigger a webhook and observe the retry behavior in the CLI
  4. Verify retries happen according to your configured schedule
  5. Update your handler to succeed and verify the next retry processes correctly
  6. Confirm your handler's idempotency logic works across retries

Testing Async and Background Jobs

If your webhook handler triggers background jobs or async processing:

  1. Receive a webhook via the CLI
  2. Check your job queue or background processor to verify the job was created
  3. Monitor your background processor logs to verify the job completes
  4. Retry the webhook with r to verify jobs are properly queued each time
  5. Test failure scenarios by making jobs fail, then verify retry logic

Debugging Webhooks Locally

Real-Time Debugging Workflow

When a webhook isn't working as expected:

  1. Webhook arrives → Appears in CLI with syntax highlighting showing full request
  2. Press d → View complete request/response details including all headers
  3. Check local logs → Review your application's console output and error messages
  4. Press o → Open the event in Hookdeck dashboard for deeper inspection
  5. Modify code → Fix the issue in your webhook handler
  6. Press r → Retry the webhook to test your fix
  7. Verify success → Confirm the webhook now processes correctly

Using CLI Interactive Mode for Debugging

The CLI's interactive mode provides powerful keyboard shortcuts for inspecting and retrying events:

  • / or k / j - Navigate between events in the history
  • r - Retry the selected event to your local server
  • d - View full request/response details for the selected event
  • o - Open the selected event in the Hookdeck dashboard
  • i - Toggle connection information (expand/collapse)

The selected event is indicated by a > character. All actions work on the currently selected event, not just the latest one.

Pro tip: Capture one webhook from your provider, then retry it repeatedly using r as you iterate on your handler logic. This is much faster than triggering new events for each code change.

Using the Dashboard for Debugging

Open events in the Hookdeck dashboard (press o in CLI) to access additional debugging tools. The dashboard provides two key views:

  • Requests: View the original inbound webhooks from providers, including raw headers and payloads
  • Events: View outbound deliveries to your local server, including all delivery attempts, response data, and retry history

Request debugging capabilities:

  • Review raw requests: Access the original inbound request exactly as received from your webhook provider
  • Check rejection reasons: Identify why requests were rejected (disabled source, verification failed, etc.)
  • View ignored events: See which connections filtered out events and why

Event debugging capabilities:

  • Inspect full payloads: View complete request and response bodies with formatted JSON
  • View event timeline: See the complete lifecycle of the event including all delivery attempts
  • Analyze retry attempts: Review each retry attempt with timing and response details
  • Check failure reasons: See detailed error messages and status codes for failed deliveries
  • Search event history: Find specific events by filtering on body content, headers, or timestamps

Debugging Common Issues

CLI Can't Connect to Local Server

Issue: Connection refused errors in CLI output

Solution: Ensure your local server is running and listening on the correct port. Check that you're using the right port number in the hookdeck listen command.

Webhooks Not Arriving

Issue: Provider shows webhooks sent, but nothing appears in CLI

Solution:

  1. Verify the CLI is running and connected
  2. Check you registered the correct Source URL with your provider
  3. Look in the Requests section to see if Hookdeck received the webhook
  4. Ensure your Source name matches what you specified in the CLI command

Signature Verification Failing

Issue: Webhooks arrive but signature verification fails locally

Solution:

  1. Verify you're using the correct signing secret from your provider
  2. Check you're reading the signature from the correct header (e.g., x-stripe-signature)
  3. For providers with timestamp-based signatures, note that Hookdeck retries may fall outside the validation window
  4. Consult your provider's documentation for the exact signature algorithm
  5. Use the dashboard to inspect the raw headers and verify the signature is present

Payload Parsing Errors

Issue: Your handler fails to parse the webhook payload

Solution:

  1. Press d in the CLI to view the raw payload
  2. Verify the Content-Type header matches what your handler expects
  3. Check for encoding issues (UTF-8, base64, etc.)
  4. Use the dashboard to inspect the exact bytes received
  5. Add error handling for malformed payloads

Timeout Issues

Issue: Webhooks timeout before your handler responds

Solution:

  1. Review your handler's processing time in the CLI output
  2. Move time-consuming operations to background jobs
  3. Return a 200 response quickly, then process asynchronously
  4. Configure appropriate timeout values in your Connection
  5. Test with the retry feature to reproduce timing issues

Best Practices for Local Webhook Testing

Use Separate Projects for Development

Create separate Hookdeck Projects for dev, staging, and prod environments. This ensures complete isolation of configuration, connections, and event history. Use infrastructure-as-code (CLI, API, or Terraform) to replicate your webhook configuration across projects, maintaining consistency while preventing accidental cross-environment routing.

Filter Events During Development

Reduce noise by filtering events based on request properties. Filtering works at the session level, making it valuable for both individual focus and team collaboration.

Individual filtering - Focus on specific events relevant to your current work:

# Only receive specific event types
hookdeck listen 3000 github --filter-headers '{"x-github-event":"pull_request"}'

# Combine multiple conditions
hookdeck listen 3000 stripe --filter-body '{"type": "charge.succeeded"}' \
  --filter-headers '{"x-stripe-signature": {"$exist": true}}'

# Use operators for complex filtering
hookdeck listen 3000 api --filter-body '{"amount": {"$gte": 100}}'

Team workflows - Multiple developers can work on the same source without interference:

# Developer A working on payment features
hookdeck listen 3000 stripe --filter-body '{"type": "charge.succeeded"}'

# Developer B working on subscription features (different machine)
hookdeck listen 3000 stripe --filter-body '{"type": "customer.subscription.updated"}'

Filtering enables:

  • Individual developers: Reduce noise from high-volume sources, test specific event types, debug scenarios without manual code filtering
  • Team collaboration: Each developer receives only events relevant to their feature work, eliminating the "shared webhook URL" problem where developers compete for events or accidentally process teammates' webhooks
  • Shared infrastructure: The entire team uses the same webhook Sources while working independently with custom filters

Keep the CLI Running

Let the CLI run in a dedicated terminal while you develop. It will automatically forward webhooks as they arrive, and the persistent connection means zero cold-start delays.

Always Implement Signature Verification

Implement webhook signature verification in your handlers during local development. This ensures your code is production-ready and catches authentication issues early.

Test with Production-Like Data

When possible, use your provider's test/sandbox mode to generate webhooks with realistic data. This helps catch issues with data types, field validation, and business logic before production deployment.

Advanced Techniques

Control Output Verbosity

Adjust how events are displayed based on your needs:

# Default - full interactive UI with keyboard shortcuts
hookdeck listen 3000 shopify

# Compact mode - simple one-line logs
hookdeck listen 3000 shopify --output compact

# Quiet mode - only shows fatal connection errors
hookdeck listen 3000 shopify --output quiet

The --output flag is useful for:

  • Reducing resource usage in high-throughput scenarios
  • Running the CLI in background processes or CI/CD pipelines
  • Minimizing terminal noise when you only care about connection failures

Test Multiple Services Simultaneously

Run multiple CLI instances to test different services:

# Service 1 on port 3000
hookdeck listen 3000 source-one

# Service 2 on port 4000 (in another terminal)
hookdeck listen 4000 source-two

This allows testing how different microservices or applications handle webhooks independently.

Automated Testing in CI/CD

The CLI includes a CI mode for automated testing in continuous integration pipelines. Use your HOOKDECK_API_KEY to authenticate without interactive login:

# Configure CLI for CI environment
hookdeck ci --api-key $HOOKDECK_API_KEY

# Start your application in the background
npm start &

# Start the CLI to forward webhooks
hookdeck listen 3000 test-source --output compact &

# Trigger test webhooks
curl -X POST "$SOURCE_URL" -H "Content-Type: application/json" -d '{"test": "data"}'

# Run your test suite that verifies webhook processing
npm test

# CLI will show delivery status in compact format:
# 2025-10-12 14:42:55 [200] POST http://localhost:3000/webhooks (34ms)

The hookdeck ci command configures the CLI for non-interactive use, making it suitable for automated testing environments. Use --output compact or --output quiet to control logging verbosity in CI logs.

Understanding Core Concepts

Sources

A Source represents a webhook provider (like Stripe or Shopify). Each Source has a unique webhook URL that you register with your provider. When you run hookdeck listen, you specify which Source's events should be forwarded to your local server.

Connections and CLI Destinations

When you run hookdeck listen 3000 source-name, the CLI looks for an existing Connection from that Source to a CLI-type Destination . If no such connection exists, the CLI creates one for you. The connection persists after the CLI session ends, allowing you to reuse it in future sessions.

Security Considerations

CLI Authentication

The CLI authenticates with your Hookdeck account. Never share your authentication tokens or run the CLI with credentials you don't control.

Local Development Only

The CLI is designed for local development. For deployed environments (staging, production), use Hookdeck's regular destination URLs, not the CLI.

Network Security

The CLI creates an outbound connection from your machine to Hookdeck. It doesn't open ports or expose your machine to inbound connections.

Webhook Signatures

Always implement webhook signature verification in your handlers, even during local development. This ensures your code is production-ready.

Next Steps

Ready to start testing webhooks locally? Follow the quickstart guide to set up the CLI and receive your first webhook:

Quickstart: Test Webhooks Locally ->

Step-by-step guide to configure the CLI and start receiving webhooks on localhost