26 min read

Test Whop Webhooks Locally with a Localtonet HTTPS Tunnel

Build a Whop app locally, expose its webhook endpoint with Localtonet, verify signed deliveries, and troubleshoot failures before deployment.

Developer Tools ยท Whop Webhooks ยท Localtonet ยท 2026

Validate signed Whop events on localhost before deploying your webhook handler

Whop must deliver webhooks to a publicly reachable HTTPS endpoint, but a development server normally listens only on localhost. In this guide, we build a local webhook route, preserve the raw request body for signature verification, expose the listener through a Localtonet HTTP tunnel, and register the resulting HTTPS endpoint with Whop. We also cover replay-safe processing, endpoint scoping, local validation, test deliveries, tunnel lifecycle, and the failure modes that commonly prevent webhook requests from reaching application code.

๐Ÿ”’ Signed payload verification before processing ๐ŸŒ Public HTTPS delivery to a local endpoint โšก Fast acknowledgement and replay-safe handling

How local Whop webhook testing works

A webhook reverses the direction of a normal API integration. Instead of your application calling Whop and waiting for a response, Whop sends an HTTP POST request to a URL that you registered. The request contains a JSON event describing something that happened, such as a successful payment, an activated membership, or an opened dispute. Your application verifies that the request was signed with the secret assigned to that webhook, acknowledges it, and performs the appropriate business action.

A server running at an address such as http://localhost:3000 is not reachable from Whop's infrastructure. The name localhost refers to the machine making the request, so entering a localhost URL in a remote webhook configuration would point back to the remote system, not to your laptop. Router network address translation, host firewalls, private addressing, and carrier-grade NAT can also prevent unsolicited inbound connections.

With Localtonet, the client running on your development machine establishes an outbound connection to one of our relay servers. An HTTP tunnel then provides a public HTTPS address and forwards requests to the local IP address and port that you selected. This does not require inbound router port forwarding, firewall changes, a VPN, or a public IP address. The tunnel remains available only while the selected device is connected and the tunnel is running.

๐Ÿ“จ Whop delivery Whop sends one signed JSON event in each HTTP POST request to the public webhook URL registered for the selected events.
๐ŸŒ Localtonet relay Our client maintains the outbound connection and forwards traffic from the assigned public HTTPS address to the configured local service.
๐Ÿงฉ Local handler Your application reads the unmodified body, verifies the Whop signature, identifies the event, records it safely, and responds promptly.
๐Ÿ” Retry-safe processing The handler uses the webhook message identifier as an idempotency key so a repeated delivery does not repeat a business operation.

The complete request path is therefore: Whop sends an HTTPS request to the public Localtonet URL, our relay passes it through the active tunnel, the Localtonet client forwards it to the local listener, and the application returns an HTTP response through the same path.

A route path is not the same as network-level path isolation

Registering a URL such as https://PUBLIC_HOST/api/webhooks/whop tells Whop which path to call, but an HTTP tunnel targets the local web service, not just that individual application route. Other routes served by the same local port may also be reachable through the public address. For stronger endpoint scoping, run a minimal webhook-only listener on a dedicated local port and tunnel that port. Otherwise, review every route on the exposed development server and make sure sensitive development tools, debug panels, administrative pages, source maps, and unauthenticated actions are not publicly available.

Prerequisites and decisions to make first

This workflow assumes that you have a Whop account and a business or app under which the webhook will be created. Whop's onboarding creates the Account that owns resources such as API keys, products, webhooks, and payments. If you have access to multiple businesses, confirm that you selected the intended one before creating credentials or webhook destinations.

You also need a local server capable of accepting HTTP POST requests. The examples below use a TypeScript route shaped for a current Next.js application because Whop documents that combination, but the networking workflow is framework-independent. Python, Ruby, Go, Java, C#, PHP, and other server environments can receive the same request as long as their handlers preserve the raw body and use a verification procedure supported by Whop.

Prepare the following before opening the tunnel:

  • A Whop account with access to the correct developer dashboard.
  • A local application with a dedicated POST route for Whop webhooks.
  • A supported Whop SDK or a verifier implemented exactly according to the current Whop webhook documentation.
  • A secret-management method such as a local environment file that is excluded from version control.
  • The Localtonet client installed and running on the machine that can reach the local service.
  • A Localtonet device token selected securely through our platform. Never paste that token into source code, screenshots, logs, or an article.
  • An available relay server or region selected from the current Localtonet dashboard rather than copied from an old example.
  • A database or durable store if the handler will perform operations that must remain idempotent across restarts.

Do you need a Whop API key?

Receiving and verifying a webhook requires the webhook signing secret. An API key is a separate credential used when your server calls Whop's API. If your handler only logs a verified test event, it may not need to call the API at all. If it retrieves resources or performs follow-up actions, keep the relevant API key on the server and grant only the permissions required by those calls.

Whop distinguishes among Account API keys, App API keys, and OAuth tokens. Account API keys act for your own Account, App API keys support apps acting against accounts that installed them, and OAuth tokens represent permissions granted by an individual user. Choose according to the integration architecture instead of treating the credential types as interchangeable.

For a TypeScript project using pnpm, Whop's documented SDK installation command is:

pnpm add @whop/sdk

Python and Ruby projects can install the corresponding official packages:

pip install whop-sdk
gem install whop_sdk
Keep each environment separate

Whop documents a separate sandbox with its own dashboard and keys. Use it when you need realistic testing without moving real money. Sandbox and production keys can look alike, so give their environment variables unambiguous names and never copy production secrets into a shared development file. Verify the selected Whop environment before sending test traffic or performing API operations.

Build a signature-aware Whop webhook endpoint

Whop webhooks follow the Standard Webhooks specification. The request includes contractually stable headers named webhook-id, webhook-timestamp, webhook-signature, and content-type. The JSON envelope includes an event identifier, event type, timestamp, API version information, account information, and an event-specific data object. Some update events can also include previous_attributes.

The most important implementation rule is to verify the exact request bytes before parsing or transforming the JSON. Parsing the body and serializing it again can change whitespace, escaping, property order, or encoding. The logical JSON may still look equivalent, but the bytes no longer match the signed content.

A current TypeScript and Next.js handler follows this structure:

import { unwrapWebhook } from "@whop/sdk/helpers";
import type { NextRequest } from "next/server";

export async function POST(request: NextRequest): Promise<Response> {
  const secret = process.env.WHOP_WEBHOOK_SECRET;

  if (!secret) {
    console.error("WHOP_WEBHOOK_SECRET is not configured");
    return new Response("Webhook configuration error", { status: 500 });
  }

  try {
    const payload = await request.text();
    const headers = Object.fromEntries(request.headers);

    const event = unwrapWebhook(payload, {
      headers,
      key: secret,
    });

    console.log("Verified Whop webhook", {
      id: event.id,
      type: event.type,
      timestamp: event.timestamp,
    });

    return new Response("OK", { status: 200 });
  } catch (error) {
    console.error("Whop webhook verification failed");
    return new Response("Invalid webhook", { status: 400 });
  }
}
Confirm SDK helper availability in your installed version

Whop's webhook documentation notes that current signature helpers may be introduced through a package release and that older SDK patterns such as a client-level webhooks.unwrap method or a webhookKey client option do not apply to current SDKs. Check the documentation and package version installed in your project before copying an import. If the documented helper is not present, use Whop's current non-SDK verification procedure exactly as published. Do not invent a cryptographic routine or restore an obsolete SDK method.

The initial handler should do as little as possible. Read the raw body once, verify the signature, record limited diagnostic metadata, and return a successful response. After this basic path works, connect it to your real event-processing layer.

Store the signing secret correctly

Whop provides a webhook signing secret when a webhook is created. Store the complete value exactly as Whop gives it to you. Do not remove its prefix, base64-encode it, paste it into source code, or expose it in client-side JavaScript. A local environment file can contain the value during development:

WHOP_WEBHOOK_SECRET=replace_with_the_secret_from_whop

The placeholder above is intentionally not a working credential. Make sure the environment file is excluded from version control and that your framework reloads environment variables after a change. Many development servers read environment files only during startup, so restart the process after adding or rotating the secret.

Acknowledge quickly and process asynchronously

Whop instructs handlers to respond in less than five seconds or the delivery may be retried. Do not make successful acknowledgement depend on sending email, provisioning access, calling several remote services, generating files, or completing other slow work. A production-oriented design verifies the request, writes the event to a durable queue or transactionally safe store, and returns a 2xx response. A worker then performs the business operation.

Do not return success before verification or before the event has been accepted into the durable mechanism responsible for processing it. Otherwise, an invalid request could enter your system, or a process crash immediately after the response could lose a valid event.

Make processing idempotent

Webhook delivery systems retry requests when they do not receive a successful response. Network uncertainty can also produce duplicate deliveries even when the original event was processed. Treat repeated delivery as normal rather than exceptional.

Use the stable value from the webhook-id header, or the verified event identifier exposed by the supported Whop verifier, as an idempotency key. Store it in a database column with a uniqueness constraint. Within a transaction, check whether that identifier was already accepted, apply or enqueue the intended state transition, and record completion. If the identifier already exists, return a successful response without repeating the business effect.

The exact database implementation depends on your stack, but the control flow should remain recognizable:

receive request
read raw body
verify signature, headers, and timestamp using the supported verifier
extract the verified message identifier
begin transaction
  insert message identifier with a uniqueness constraint
  if it already exists:
    commit and acknowledge as already handled
  otherwise:
    record or enqueue the required business action
commit
return a successful response promptly

Timestamp checks are also important for reducing replay risk. Use the policy implemented or recommended by the current Whop verifier. Do not guess a tolerance window, because accepted tolerances can be security-sensitive and may change with the supported implementation.

Validate the listener before exposing it

Testing locally first separates application problems from tunnel and provider problems. Start the application using the command defined by your project, then confirm the actual listening port from its startup output. Do not assume that every Next.js project, framework, container, or development proxy uses the same port.

If your route is /api/webhooks/whop and your application reports that it is listening on port 3000, you can confirm that the route is reachable with:

curl -i -X POST http://127.0.0.1:3000/api/webhooks/whop \
  -H "Content-Type: application/json" \
  --data '{"local_probe":true}'

This is deliberately unsigned and should not be accepted as a valid Whop event. A response such as 400 confirms that the route exists and rejects unverifiable input. A 404 suggests that the route path is wrong, while a connection refusal means that the process is not listening at that address and port.

Also verify any harmless application health route directly if your project has one. Do not weaken the webhook handler by adding a development-only branch that skips signatures. A local bypass tends to survive longer than expected and can become a production vulnerability.

Local result What it usually indicates Next check
Connection refused No process is listening at the tested IP address and port. Start the app and read its actual bind address and port from the startup log.
404 Not Found The server is reachable, but the route path or framework routing convention is incorrect. Compare the URL with the file-based or explicit route configured by the application.
405 Method Not Allowed The route exists but does not accept POST requests. Add or enable the POST handler without exposing unnecessary methods.
400 Invalid webhook The route was reached and rejected the unsigned local probe. This is expected for the curl example. Continue with an authentic Whop test delivery.
500 Configuration error The signing secret or another server-side requirement is missing. Load the environment variable and restart the development server.

Expose the local listener with a Localtonet HTTP tunnel

Once the endpoint works locally, create an HTTP tunnel that points to the verified local target. Use the machine running the Localtonet client when specifying the local address. If the application runs in a container, virtual machine, or another device on your LAN, the target must be an address that is reachable from that client.

Follow this sequence in our platform:

1

Install and run the Localtonet client

Run the client on the development machine or another device that can reach the webhook listener. Confirm that the client is connected before diagnosing the tunnel itself.

2

Select the authenticated device

Use the device-specific authentication token associated with the client. Select it through the dashboard and keep the token private. Never copy an example token from another device.

3

Choose an available relay server

Select a server or region currently offered in the dashboard. Availability can vary, so do not rely on a hardcoded server code from an old tutorial.

4

Create an HTTP tunnel to the local service

Enter the local IP address and the port confirmed during local validation. For a service on the same machine, this is normally a loopback target such as 127.0.0.1 plus the actual listening port. Select the desired HTTP process type from the options currently available to your account.

5

Start the tunnel

Creating the configuration does not start it. Use the Start button and wait until the tunnel and selected client are connected.

6

Copy the assigned public HTTPS URL

Use the HTTPS address displayed for the running tunnel. Append the exact webhook route path, such as /api/webhooks/whop, without changing the path used by your local application.

HTTP tunnels can use a generated random subdomain, a selected subdomain where supported, or a custom domain. The process types serve the same content at a public HTTPS address. For temporary testing, a generated address is often sufficient. If the public address changes between sessions, update the webhook destination in Whop before sending another test event.

Tunnel creation and tunnel availability are different states

Saving a tunnel does not make it active. The Localtonet client must be connected and the tunnel must be running. Stopping the client, putting the development machine to sleep, losing its network connection, or pressing Stop makes the public endpoint unavailable until connectivity and the tunnel are restored.

Register the public HTTPS endpoint in Whop

After the Localtonet tunnel is running, construct the complete destination by combining the assigned HTTPS origin and your webhook path. For example, if the route is /api/webhooks/whop, the destination has this shape:

https://PUBLIC_LOCALTONET_HOST/api/webhooks/whop

Use the real URL assigned in your dashboard. Do not include placeholder text, a localhost address, or the local port in the public URL unless the assigned public endpoint explicitly includes one.

1

Open the correct Whop environment and business

Confirm whether you are working in sandbox or production, then select the business or app that should own the webhook.

2

Open Developer and create a webhook

In the Whop dashboard, open the Developer area and its Webhooks page, then select the action to create a webhook.

3

Enter the full Localtonet HTTPS destination

Paste the public HTTPS URL with the exact route suffix. Check for missing slashes, duplicated path segments, accidental spaces, and stale tunnel hostnames.

4

Select the API version and required events

Keep the documented API version selection appropriate for your integration and subscribe only to event types that the application actually handles. Version pins preserve the event and header contract that your integration was built against.

5

Save and store the signing secret

Copy the webhook signing secret from Whop and store it as WHOP_WEBHOOK_SECRET in the server environment. Treat it as a credential and restart the local process if required for environment changes to load.

6

Send a Whop test event

Use the webhook row actions in Whop to send a test event while the local application, Localtonet client, and tunnel are all running.

Whop versions webhook envelopes by date. For webhooks pinned before the documented 2026-08-14 change, and for webhooks without that pin, the account field may appear as company_id rather than account_id. Avoid assuming that copied sample payloads exactly match every version. Parse according to the version selected for your webhook and test against payloads generated by that environment.

Verify the complete signed delivery

A successful browser request to the public URL is not enough. Browsers normally issue a GET request, while Whop sends a signed POST containing required headers and a JSON body. The meaningful test is a delivery initiated by Whop.

Keep the application log visible, trigger a Whop test event, and verify all of the following:

  • The request reaches the intended POST route rather than a generic page or framework fallback.
  • The handler receives the stable webhook headers, including the message identifier, timestamp, and signature.
  • The raw body is passed directly to the supported verifier.
  • The verifier succeeds with the secret belonging to this exact webhook and environment.
  • The application logs only limited metadata, not the signing secret or an unnecessarily complete customer payload.
  • The endpoint returns a 2xx response in less than five seconds.
  • A repeated copy of the same verified message does not repeat fulfillment, access grants, emails, credits, or database transitions.

For the first end-to-end test, logging the verified event identifier and type is enough. Add event-specific behavior only after the signature and acknowledgement path is reliable. This makes it easier to distinguish a transport problem from a business-logic failure.

A webhook test is complete only when you can trace the same request through four checkpoints: Whop reports the delivery, the Localtonet tunnel is running, the local server logs a verified event, and Whop receives the successful HTTP response. If any checkpoint is missing, use the last confirmed stage to narrow the investigation.

Operate the development workflow safely

Keep the exposed surface small

The safest development target is a dedicated local listener that exposes only the webhook route and perhaps a minimal health endpoint. If you tunnel a complete application server, review its other routes first. Development builds sometimes expose stack traces, framework diagnostics, source maps, test accounts, or tools that were never intended for the public internet.

Signature verification protects the webhook handler from unauthenticated event processing, but it does not automatically protect unrelated routes on the same port. Continue using normal authentication, authorization, least privilege, and input validation throughout the application.

Log metadata rather than secrets

Useful webhook diagnostics include the verified message identifier, event type, processing state, response status, and internal correlation identifier. Avoid logging the signing secret, API keys, authorization headers, complete payment details, or full personal records. If temporary payload logging is unavoidable during development, restrict access to the logs and remove or redact them after the issue is understood.

Separate acknowledgement from fulfillment

A development handler may print an event and return success, but production business logic should normally use a durable queue or transactional outbox. This prevents a temporary email, database, or downstream API failure from making the incoming HTTP request exceed Whop's response window.

Define explicit behavior for unknown event types. A forward-compatible handler commonly verifies and records the event, ignores types it does not support, and returns success rather than treating every new event as a server error. However, it should also alert developers when a subscribed event lacks a handler.

Rotate secrets deliberately

If a webhook secret appears in a screenshot, repository, client bundle, shared chat, or public log, treat it as compromised. Replace it through the supported Whop workflow, update the server environment, restart the process, and retest. Do not rely on deleting the visible copy alone.

Stop temporary access when testing ends

Stop the Localtonet tunnel when the public endpoint is no longer required. Delete obsolete tunnel configurations and Whop webhook destinations when they will not be reused. If you retain the webhook while stopping the tunnel, expect deliveries to fail until the endpoint becomes available again.

Layer Credential or identifier Safe handling rule
Localtonet device Device-specific authentication token Select it securely and never expose it in code, logs, screenshots, or documentation.
Whop webhook Webhook signing secret Keep it server-side and use it only to verify requests for the corresponding webhook.
Whop API Account or App API key Grant least privilege, keep it out of browser code, and separate sandbox from production.
Webhook delivery Webhook message identifier Persist it as an idempotency key to prevent duplicate business effects.

Troubleshoot failed Whop webhook deliveries

Whop reports a connection failure

First confirm that the Localtonet client is connected and that the tunnel was started. A saved configuration is not necessarily running. Verify that the development machine is awake and online. Then open the public origin or a harmless health route, if one exists, to determine whether the public endpoint can reach the local service.

If the tunnel is active but the target refuses the connection, compare the configured local IP address and port with the application's startup output. Containerized applications may bind only inside a container, and a service running on another machine may not be reachable through 127.0.0.1 from the Localtonet client device.

The public URL returns 404

A 404 generally means the request reached a web server but not the intended route. Check the full destination registered in Whop, including its path. Confirm whether the framework uses /api/webhooks/whop, /webhooks/whop, or another route actually created by your project. Also check for a base path, reverse-proxy prefix, trailing-slash rule, or duplicate route segment.

The endpoint returns 405

Whop uses POST. A route that only exports or registers GET will reject the request. Ensure that the selected framework file defines a POST handler and that middleware is not rewriting the request to a page that supports only browser navigation.

Signature verification fails

Confirm these items in order:

  1. The server read the raw request body exactly once.
  2. No JSON parser, middleware, proxy, or request transformation changed the body before verification.
  3. The application loaded the complete signing secret without trimming or re-encoding it.
  4. The secret belongs to the webhook that sent the request.
  5. The sandbox webhook is not being verified with a production secret, or the reverse.
  6. The required webhook headers reached the application unchanged.
  7. The installed SDK version contains the helper used by the code, or the current official non-SDK procedure is implemented exactly.
  8. The development server was restarted after the environment variable changed.

Do not fix verification failures by disabling signature checks. An unsigned request should fail, including the local curl probe shown earlier.

Whop times out even though processing finishes

The handler is probably doing too much before responding. Move slow operations behind a durable queue or background-processing boundary. Measure the full request duration, including database locks, DNS lookups, API calls, email delivery, and cold starts. Whop expects a response in less than five seconds.

The same business action happens twice

Acknowledgement alone does not prevent duplicate processing. Store the verified webhook message identifier with a uniqueness constraint and make the business transition transactional. An in-memory set is useful for a short experiment but does not survive process restarts and does not coordinate multiple workers.

The endpoint worked earlier but stopped

Check whether the Localtonet client disconnected, the tunnel was stopped, the machine slept, the local server restarted on another port, or the public hostname changed after a tunnel configuration change. Compare the currently assigned HTTPS address with the URL registered in Whop.

The test works, but a real event does not

Verify that the production event type is among the webhook's subscriptions and that the event occurred in the same Whop environment and business that owns the webhook. Then inspect your event dispatch logic. A test may prove transport and signature verification while still using a different event type from the real operation.

A version-dependent field is missing

Inspect the webhook's API version date. The account field can differ across the documented version boundary, and previous_attributes is optional and limited to update events that capture changes. Code defensively against optional fields and test the exact pinned version rather than relying on a sample from another integration.

Frequently asked questions

Can Whop send a webhook directly to localhost?

No. A localhost address is local to the machine making the request and is not a public destination for Whop. With Localtonet, the client creates an outbound connection and an HTTP tunnel provides a public HTTPS address that forwards requests to your local IP address and port.

Does registering a webhook path expose only that path?

Not by itself. The registered path tells Whop where to send requests, while the HTTP tunnel forwards to the selected local web service. Other routes on that service may remain reachable. Use a webhook-only listener on a dedicated local port when strict endpoint scoping is required.

Why must the handler read the raw request body?

The signature covers the delivered content. Parsing and serializing JSON can change its byte representation even when the resulting object appears equivalent. Pass the original body and headers to Whop's supported verifier before using the event data.

Is the Whop API key the same as the webhook signing secret?

No. The webhook signing secret verifies incoming webhook requests. An API key authorizes outgoing calls from your server to Whop's API. Keep both server-side, assign least privilege to API keys, and never substitute one credential for the other.

How quickly should the endpoint respond?

Whop instructs webhook handlers to respond in less than five seconds. Verify the request, accept it into a durable processing mechanism, and return a successful response promptly. Perform slow fulfillment or notification work asynchronously.

How should duplicate Whop webhook deliveries be handled?

Treat duplicates as expected. Persist the verified webhook message identifier with a uniqueness constraint and ensure that the corresponding business operation is applied only once. Return success for an already processed valid event instead of repeating its side effects.

Does the Localtonet tunnel remain online after the client stops?

No. The tunnel is available only while the selected client or device is connected and the tunnel is running. Stopping the tunnel, closing the client, losing connectivity, or sleeping the development machine makes the endpoint unavailable.

Should testing use Whop sandbox or production?

Prefer Whop's separate sandbox when testing workflows that could otherwise move real money or alter production resources. Sandbox and production have separate dashboards and credentials. Label their environment variables clearly and create the webhook in the same environment that generates the test event.

Test your Whop webhook with Localtonet

Run your signature-aware endpoint locally, connect the correct development device, and create an HTTP tunnel that gives Whop a public HTTPS destination without inbound router port forwarding or deploying unfinished code.

Get Started Free โ†’

Localtonet is a secure multi-protocol tunneling and proxy platform designed to expose localhost, devices, private services, and AI agents to the public internet supporting HTTP/HTTPS tunnels, TCP/UDP forwarding, mobile proxy infrastructure, file server publishing, latency-optimized game connectivity, and developer-ready AI agent endpoint exposure from a single unified control plane.

support