Introduction to Supabase webhook testing
Supabase webhook testing is the process of sending sample webhook requests to a Supabase Edge Function and checking whether it correctly receives, validates, parses, and processes them. The goal is to catch problems before a real webhook provider sends production traffic.
Webhook endpoints are different from ordinary API routes because they must handle third-party HTTP POST requests, verify webhook signatures, process JSON payloads, and return the right status codes quickly enough to avoid unnecessary retries. In an event-driven architecture, that usually means the endpoint must be reliable, idempotent, and easy to debug.
A good workflow covers both local development and production deployment. Locally, you can replay payloads, inspect request headers, and test parsing without exposing live credentials. In production, you confirm the deployed function behaves the same way with real requests, real Supabase secrets, and real database writes.
For a broader overview of webhook validation workflows, see webhook testing for developers and how to test webhook integrations.
What you need before you start
Before testing webhooks with Supabase, gather the basics:
- A Supabase project
- The Supabase CLI
- A local environment that can run Deno Edge Functions
- A webhook provider that can send test events
- Saved JSON payloads for replaying requests
- A shared secret for signature verification
- A tool such as cURL or Postman for sending sample requests
If you plan to test a live provider against your laptop, you may also need a tunneling tool such as ngrok. If you are only replaying requests locally, a tunnel is optional.
Store secrets in environment variables during local development and in Supabase secrets before deployment. That keeps credentials out of source control and makes it easier to switch between test and production environments.
For setup guidance, see local webhook testing and Supabase integration setup.
How Supabase webhooks and Edge Functions work
A webhook provider sends an HTTP POST request to your Supabase Edge Functions endpoint. The function reads the raw request body, checks the Content-Type header, and then parses the JSON payload.
In Deno, the handler works with standard Request objects, so you should read the body once and avoid reusing a consumed stream. Typical webhook logic includes:
- Checking the request method
- Validating the
Content-Typeheader - Verifying the webhook signature
- Parsing the request body
- Routing the event to the correct handler
- Writing results to the database if needed
That routing step matters in event-driven architecture because different event types may require different actions, such as updating a user record, creating an order, or storing an audit log.
How do I test webhooks locally with Supabase Edge Functions?
Use the Supabase CLI to run your function locally:
supabase functions serve webhook-handler
Then send requests with cURL or Postman. This lets you test request formatting, headers, parsing, and response codes without deploying anything.
Example cURL request:
curl -i http://127.0.0.1:54321/functions/v1/webhook-handler \
-X POST \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: test-signature" \
-d '{"event":"payment.succeeded","data":{"id":"evt_123"}}'
Local testing is best when you want to replay a saved payload, check request body parsing, confirm signature verification logic, verify status codes, and test database writes safely.
If a real provider must reach your machine, use ngrok or another tunneling tool to forward traffic to localhost. That is useful when the provider only accepts public URLs.
For more examples, see webhook endpoint testing and webhook endpoint testing online.
Do I need a tunneling tool like ngrok to test Supabase webhooks?
Not always. You only need ngrok or a similar tunneling tool when an external webhook provider must send live requests to your local machine.
You do not need a tunnel if you are replaying saved JSON payloads with cURL or Postman, or if you are testing parsing, validation, and idempotency logic offline.
You do need a tunnel if you are testing a provider that requires a public callback URL or debugging provider-side retries and signature generation.
How do I verify a webhook signature in Supabase?
Signature verification should happen before any database write or downstream action.
A common pattern is:
- Read the raw request body
- Extract the signature from a request header
- Compute an HMAC using the shared secret
- Compare the computed value with the signature sent by the provider
- Reject the request if the values do not match
Example Deno-style logic:
const body = await req.text();
const signature = req.headers.get("X-Webhook-Signature");
const secret = Deno.env.get("WEBHOOK_SECRET");
if (!signature || !secret) {
return new Response("Missing signature or secret", { status: 400 });
}
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
const expected = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
// Compare expected with the provider signature format here.
The exact signature format depends on the webhook provider, so check its documentation before implementing the comparison.
How do I parse webhook payloads in an Edge Function?
Parse the request body only after you have confirmed the method and content type.
A simple pattern is:
const rawBody = await req.text();
let payload: unknown;
try {
payload = JSON.parse(rawBody);
} catch {
return new Response("Invalid JSON", { status: 400 });
}
If you expect a known structure, validate the fields before using them. For example, you might check for event, data, created_at, or id.
Do not assume every payload has the same shape. Some providers send nested objects, while others send arrays or envelope formats. If the payload is large or inconsistent, log the event type and a small subset of fields instead of the full body.
What are the most common webhook testing errors?
The most common issues are usually easy to diagnose from the response code and logs:
- 400 Bad Request: malformed JSON, empty body, or missing required fields
- 401 Unauthorized: signature verification failed
- 405 Method Not Allowed: the provider used the wrong method, often
GETinstead ofPOST - 415 Unsupported Media Type: the
Content-Typeheader is wrong - 500 Internal Server Error: unhandled exception, missing secret, or failed database write
Other common causes include reading the body twice, verifying the signature after parsing and reserializing the payload, using the wrong secret in Supabase secrets, and forgetting to update the provider callback URL.
What should I log when debugging webhook requests?
Use structured logging and keep the output focused. Log:
- Request ID or correlation ID
- Request method
- Relevant request headers
Content-Type- Event type
- Signature verification result
- Parsing result
- Final status code
Avoid logging full secrets, API keys, or sensitive payload fields. If you need to inspect the body, log only a redacted subset or a hash.
The Supabase dashboard logs are useful for confirming whether the function was invoked and what it returned. If the provider says it delivered a request but you see nothing in the dashboard, the issue may be upstream, such as a bad URL, tunnel failure, or provider-side rejection.
How do I handle duplicate webhook deliveries?
Duplicate deliveries are normal because many providers use retry logic when they do not receive a timely success response.
The safest fix is idempotency. Store a unique event ID before processing, then skip any event you have already handled. You can implement this with a database table that records the provider event ID, event type, received timestamp, and processing status.
If the provider does not send a stable event ID, derive one from a combination of fields that uniquely identify the event. Make sure the deduplication check happens before any irreversible database writes.
How do I send sample webhook payloads to Supabase?
You can send sample payloads with cURL, Postman, a saved JSON file, or a provider’s own test mode or dashboard.
Example with a file:
curl -i http://127.0.0.1:54321/functions/v1/webhook-handler \
-X POST \
-H "Content-Type: application/json" \
--data @sample-payload.json
This is useful for testing request body parsing, event routing, and error handling without waiting for a live provider event.
What status codes should a webhook endpoint return?
Use status codes that clearly tell the provider whether the request was accepted:
- 200 OK or 204 No Content: request processed successfully
- 400 Bad Request: invalid payload or missing required data
- 401 Unauthorized: signature or authentication failed
- 405 Method Not Allowed: wrong HTTP method
- 415 Unsupported Media Type: unsupported
Content-Type - 500 Internal Server Error: unexpected server failure
Many providers retry on non-2xx responses, so return a success code only after you have completed the work you want to guarantee.
How do I store webhook secrets securely in Supabase?
Store secrets in Supabase secrets rather than hardcoding them in your function. During local development, use environment variables; in deployed projects, use the Supabase secret store so the function can read values securely at runtime.
Keep these values separate when possible:
- Webhook signing secret
- Provider API keys
- Database credentials
If you rotate a secret, update both the provider and the Supabase project together. A mismatch between the two is one of the fastest ways to break signature verification.
How do I know if my webhook endpoint is receiving requests?
Check three places:
- The Supabase dashboard logs for the Edge Function
- The provider’s delivery history or test console
- Your own structured logging output
If the provider shows a successful delivery but Supabase shows nothing, the request may be blocked before it reaches the function. Common causes include a wrong callback URL, an expired tunnel URL, incorrect method or headers, or provider-side signature failure.
If Supabase logs show the request but your code still fails, inspect the response body and status code to see whether the problem is parsing, signature verification, or downstream processing.
How do I deploy a tested Supabase Edge Function?
Once local testing is stable, deploy the function with the Supabase CLI:
supabase functions deploy webhook-handler
Before switching production traffic, make sure the function passes local tests, required Supabase secrets are set, the provider callback URL points to the deployed endpoint, the production shared secret matches the provider configuration, and logging is enabled in the Supabase dashboard.
After deployment, send one final test event from the provider’s dashboard or test mode and confirm the full flow: inbound request, signature verification, event routing, and database writes.
Local vs production webhook testing
Local testing and production testing solve different problems.
Local development is best for fast iteration. You can replay payloads, test parsing, and debug signature verification without affecting real users.
Production deployment testing confirms that the deployed Edge Function works with the real provider URL, real secrets, and real traffic patterns. It also helps you verify retry behavior, idempotency, and database writes under realistic conditions.
A safe workflow is:
- Test with saved payloads locally
- Test with a tunnel if the provider needs a public URL
- Deploy to a staging or production environment
- Send a provider test event
- Confirm logs, status codes, and database changes
Final checklist for Supabase webhook testing
- Verify the request method is
POST - Check
Content-Type - Parse the raw request body once
- Validate the webhook signature with HMAC and a shared secret
- Use structured logging
- Return the right status codes
- Handle duplicate deliveries with idempotency
- Store secrets in Supabase secrets
- Use ngrok only when a public callback URL is required
- Confirm requests appear in the Supabase dashboard logs
- Test both local development and production deployment paths
For additional reading, see webhook delivery testing and webhook testing tool for developers.