How to Verify Webhook Signatures and Test Reception (LINE, Stripe, GitHub, Slack, and More)
A webhook is a mechanism through which a service sends HTTP requests to your server. During development, it can be hard to tell whether requests are arriving at all, what their contents look like, or why signature verification is failing. This page summarizes what to check for each provider. If you are new to webhooks, start with Getting started with Hookbako.
First, check whether requests are arriving
- Generate a receiving URL in Hookbako and paste it into the provider's webhook destination setting.
- Send a test or perform a real action from the provider's dashboard.
- If a request arrives, it appears in the list. If not, review the provider's settings: check the URL for typos, whether webhooks are enabled, and which event types are sent.
Once you know what the contents look like, you can use the curl command generated by Hookbako to resend the same request to your development server (such as localhost) and check its behavior.
Common causes of signature verification failures
- Calculating the signature after parsing the body. The signature is calculated over the exact bytes that were sent. If a framework parses the body as JSON and then converts it back to a string, whitespace or field order can change, causing a mismatch. Calculate it using the body exactly as received (the raw body).
- Using the wrong key. Keys may differ between production and test environments, and between endpoints created in the dashboard and CLI forwarding.
- Confusing the format. Check whether it uses Base64 or hexadecimal (hex), and check any prefix (
sha256=,v0=, orv1,). - Time differences. Timestamped signatures are normally rejected if they are too old (often more than 5 minutes).
How signatures work for each provider
| Provider | Signature header | String to sign | Format | Key |
|---|---|---|---|---|
| LINE | x-line-signature | Body | HMAC-SHA256 · Base64 | Channel secret |
| Stripe | Stripe-Signature (t=…,v1=…) | {t}.{body} | HMAC-SHA256 · hex | Signing secret (whsec_…) |
| Shopify | X-Shopify-Hmac-SHA256 | Body | HMAC-SHA256 · Base64 | App client secret |
| GitHub | X-Hub-Signature-256 (sha256=…) | Body | HMAC-SHA256 · hex | Webhook Secret |
| Slack | X-Slack-Signature (v0=…) | v0:{X-Slack-Request-Timestamp}:{body} | HMAC-SHA256 · hex | Signing Secret |
| makeshop | x-makeshop-signature | {x-makeshop-request-timestamp}:{body} | HMAC-SHA256 · Base64 | Secret key |
| Standard Webhooks | webhook-signature (v1,…) | {webhook-id}.{webhook-timestamp}.{body} | HMAC-SHA256 · Base64 | Base64-decoded value after whsec_ |
| kintone | None | — | — | — |
In Hookbako, select a received request and enter the key to verify it using the methods above, entirely within your browser. The key is not sent to the server.
LINE(Messaging API)
Paste the receiving URL into the Webhook URL field in the Messaging API settings of the LINE Developers console. Clicking "Verify" sends a request with an empty events array. Returning 200 for this request completes verification successfully.
Message events contain a replyToken. It can be used to reply only once and must be used immediately after receipt. Redelivered events have deliveryContext.isRedelivery set to true, so use webhookEventId to avoid processing the same event twice.
Stripe
Add an endpoint under Webhooks in the dashboard and paste in the receiving URL. The t in Stripe-Signature is a timestamp; by default, signatures older than 5 minutes are rejected. The same event may arrive more than once, so deduplicate by event ID before processing. Events from the test environment have livemode set to false.
Shopify
The notification type is identified by X-Shopify-Topic (for example, orders/create), and the shop by X-Shopify-Shop-Domain. The same notification may arrive more than once, so deduplicate using X-Shopify-Webhook-Id.
GitHub
In the repository's Settings › Webhooks, paste the receiving URL into Payload URL. A ping event arrives immediately after registration. If no Secret is entered, X-Hub-Signature-256 is not included. The event type is in X-GitHub-Event, and the ID for each delivery is in X-GitHub-Delivery.
Slack
Registering a Request URL for the Events API sends a request with type set to url_verification. Return the body’s challenge value unchanged to complete registration. Slack requires a response within 3 seconds, so defer time-consuming processing. Hookbako's "Response" settings let you return a fixed body.
makeshop
The signature is generated from a string consisting of the timestamp and body joined by :. The official guidance recommends rejecting timestamps older than 5 minutes.
Standard Webhooks (Svix, Resend, and Others)
This format uses three headers: webhook-id, webhook-timestamp, and webhook-signature. The key is the byte sequence obtained by Base64-decoding the part after whsec_. Requests with the same webhook-id are redeliveries of the same notification.
kintone
kintone webhooks do not have signatures. The body's type (such as ADD_RECORD or UPDATE_RECORD) and app tell you what happened. Keep the receiving URL private and, if necessary, restrict requests by the sender's IP address.
Use Hookbako
Hookbako is free to use, with no registration required. Inboxes are automatically deleted after 7 days, and only the latest 100 requests are stored.