Test Webhooks on Localhost with Hookdeck CLI Quickstart

View as Markdown

In this quickstart, you'll learn how to test and debug webhook integrations on your localhost using the Hookdeck CLI. You'll set up a local development environment that receives real webhooks from webhook providers, inspect requests in real-time, and debug your webhook handlers without deploying code.

Learn the key concepts ->

Read the overview to understand how local webhook testing fits in your development workflow.

You will walk through these steps:

  1. Install and authenticate the Hookdeck CLI
  2. Start your local development server
  3. Connect the CLI to forward webhooks to localhost
  4. Send a test webhook and see it arrive locally
  5. Test with a real third-party provider
  6. Inspect and debug webhooks
  7. Retry webhook events

Create a Hookdeck account and Setup the CLI

Sign up for a free Hookdeck account if you don't have one already.

From sign up, create your organization and then you can Skip to Dashboard and come back to this guide.

Install the Hookdeck CLI.

  npm install hookdeck-cli -g
  
  
  yarn global add hookdeck-cli
  
  
    brew install hookdeck/hookdeck/hookdeck
    
    
  1.     scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
        
        
  2.   scoop install hookdeck
      
      
  1. Download the latest release's tar.gz file.

  2.     tar -xvf hookdeck_X.X.X_linux_x86_64.tar.gz
        
        
  3.   ./hookdeck
      
      

Run hookdeck login in your terminal to authenticate.

hookdeck login

The CLI will open your browser for authentication. Once complete, you'll see a confirmation in your terminal.

Start your local development server

Set up a simple webhook handler on your local machine:

Run the following to get the code and install the app's dependencies:

git clone https://github.com/hookdeck/quickstarts.git
cd quickstarts/typescript/inbound
npm i

Then, run the application:

npm run dev

You will see output as follows:

> inbound@1.0.0 dev
> nodemon index.ts

[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `ts-node index.ts`
🪝 Server running at http://localhost:3030

Get the Hookdeck quickstart code:

git clone https://github.com/hookdeck/quickstarts.git
cd quickstarts/python/inbound

Create a new virtualenv, activate it, and Install the application dependencies:

virtualenv venv
source venv/bin/activate
pip install -r requirements.txt

Run the app:

python app.py

The output will look as follows:

python app.py
 * Serving Flask app 'app'
 * Debug mode: on
INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
INFO:werkzeug:Press CTRL+C to quit
INFO:werkzeug: * Restarting with stat
WARNING:werkzeug: * Debugger is active!
INFO:werkzeug: * Debugger PIN: 530-477-256

Get the Hookdeck quickstart code:

git clone https://github.com/hookdeck/quickstarts.git
cd quickstarts/go/inbound

Run the app:

go run .

The output will look as follows:

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /webhook                  --> main.main.func1 (3 handlers)
🪝 Server running at http://localhost:3030[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :3030

Get the Hookdeck quickstart code:

git clone https://github.com/hookdeck/quickstarts.git
cd quickstarts/dotnet/inbound

Run the app:

dotnet run

The output will look as follows:

dotnet run
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:3030
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: {path}

Connect the CLI to your local server

With your local server running, start the Hookdeck CLI to get a public URL to be used as the webhook endpoint with your webhook provider and to forward webhooks to your local server:

In a new terminal window, run:

hookdeck listen 3030 test-webhooks --path /webhook

You'll see output like:

●── HOOKDECK CLI ──●

Listening on 1 source 1 connection [i] Collapse

test-webhooks
  Requests to https://hkdk.events/src_{source_id}
└─ Forwards to http://localhost:3030/webhook

💡 View dashboard to inspect, retry & bookmark events: https://dashboard.hookdeck.com/events/cli?team_id=...

Events [↑↓] Navigate ──────────────────────────────────────────────────────────

> Waiting for events | [i] Expand connection info [q] Quit

What this does:

  1. Creates a Source named test-webhooks that represents the webhook sender (Stripe, Shopify, GitHub, or any webhook provider). Use URL prefixed with "Requests to" (the Source URL) as the webhook URL with your provider.
  2. Creates a Destination named local-dev that defines where webhooks are delivered. It forwards incoming webhooks to your local server at http://localhost:3030/webhook via the CLI.
  3. Creates a Connection that routes events from the Source through the CLI Destination to your machine.

Keep this terminal window open. The CLI needs to stay running to forward webhooks.

Send a test webhook

Now that everything is connected, send a test webhook to see it arrive on your local server:

In a third terminal window, send a test webhook to your Source URL:

curl --location '{YOUR_SOURCE_URL}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "type": "order.created",
    "timestamp": "2023-11-18 17:23:30",
    "email": "customer@example.com",
    "description": "Growth plan",
    "plan_id": "growth",
    "payment_status": "pending"
}'

Replace {YOUR_SOURCE_URL} with the Source URL (the one prefixed with "Requests to" in the terminal output).

What happens:

  1. Hookdeck receives the webhook at your Source URL
  2. Hookdeck tunnels the event to the CLI
  3. CLI logs the event in your terminal
  4. CLI forwards the request to your local server at http://localhost:3030/webhook
  5. Your server processes the webhook and returns a response

You should see output in three places:

In your cURL terminal:

{
  "status": "SUCCESS",
  "message": "Request successfully handled",
  "request_id": "req_xyz789"
}

In your CLI terminal:

●── HOOKDECK CLI ──●

Listening on 1 source 1 connection [i] Expand

Events [↑↓] Navigate ─────────────────────────────────────────────────

> 2025-10-27 16:51:39 [200] POST http://localhost:3000/webhook (11ms) → https://dashboard.hookdeck.com/events/evt_{id}  

In your local server terminal:

{
  webhook_received: '2023-11-19T20:40:29.767Z',
  body: {
    type: 'order.updated',
    timestamp: '2023-11-18 17:25:15',
    email: 'customer@example.com',
    description: 'Hookdeck Growth plan',
    plan_id: 'growth',
    payment_status: 'paid'
  }
}
INFO:app:webhook_received 2023-11-19T20:40:29.767Z {
  "type": "order.updated",
  "timestamp": "2023-11-18 17:25:15",
  "email": "customer@example.com",
  "description": "Hookdeck Growth plan",
  "plan_id": "growth",
  "payment_status": "paid"
}
INFO:werkzeug:127.0.0.1 - - [19/Nov/2023 20:36:53] "POST /webhook HTTP/1.1" 200 -
2024/01/12 16:41:45 webhook_received: {
  type: 'order.updated',
  timestamp: '2023-11-18 17:25:15',
  email: 'customer@example.com',
  description: 'Hookdeck Growth plan',
  plan_id: 'growth',
  payment_status: 'paid'
}
{ webhook_received = 2024-09-27T10:25:03.0830880Z, body = {
    "type": "order.updated",
    "timestamp": "2023-11-18 17:25:15",
    "email": "customer@example.com",
    "description": "Hookdeck Growth plan",
    "plan_id": "growth",
    "payment_status": "paid"
} }

Test with a real provider

Now that you've tested with manual webhooks, connect a real third-party provider:

Register your Source URL with your webhook provider (Stripe, Shopify, GitHub, etc.). The specific steps depend on the provider - check their documentation for "webhook configuration" or "webhook URL".

Trigger an event in the provider's platform (create an order, push code, etc.).

Watch as the real webhook flows through Hookdeck and arrives at your local development server.

Inspect and debug webhooks

The CLI provides powerful debugging tools right in your terminal:

Use the interactive CLI controls to inspect webhooks:

  • Press d to view full request/response details for the selected event
  • Press / (or k/j) to navigate between webhook events
  • Press o to open an event in the dashboard for deeper inspection

For advanced debugging workflows, troubleshooting, and production best practices, see the complete guide.

Retry webhook events

One of the most powerful features for local development is event retry. Once Hookdeck has captured a webhook, you can retry it to your local server instantly.

The CLI provides instant retry through keyboard shortcuts:

In your running CLI session, use the interactive controls:

  1. Navigate events: Press or to select the event you want to retry
  2. Retry: Press r to instantly retry the selected event to your local server
  3. View details: Press d to see full request/response data

The selected event is indicated by a > character. When you press r, the webhook is immediately retried to your local server, appearing in your CLI output and server logs.

You can also retry events from the Hookdeck dashboard by clicking the Retry button on any event.

Next steps

Now that you're successfully testing webhooks locally, explore more advanced topics:

Advanced CLI Usage & Troubleshooting ->

Learn about testing failure scenarios, advanced CLI features, and common issues

Configure Source Authentication ->

Learn how to set up authentication for your webhook sources

Configure Retry Behavior ->

Learn how to set up automatic retries for failed webhooks

Filter Webhook Events ->

Route only specific webhook events to your handlers