Verify webhook signatures
Learn how to verify that webhook deliveries come from TheirStack by validating the HMAC-SHA256 signature included in each request.
When you configure a signing secret for your webhook, TheirStack signs every delivery with an HMAC-SHA256 signature so you can verify the payload was sent by TheirStack and has not been tampered with.
How it works
- You set a signing secret (minimum 16 characters) when creating or editing a webhook.
- For every delivery, TheirStack computes an HMAC hex digest of the raw JSON body using your secret and includes it in the
X-TheirStack-Signature-256header:
X-TheirStack-Signature-256: sha256=<hex_digest>- Your endpoint recomputes the same HMAC and compares the two values using a timing-safe comparison to prevent timing attacks.
Setting up a signing secret
You can add a signing secret when creating a new webhook or update an existing one:
- In the app — use the "Signing Secret" field in the webhook creation/edit form. You can generate a random secret or provide your own (minimum 16 characters).
- Via the API — include a
secretfield in the request body when calling POST /v0/webhooks or PATCH /v0/webhooks/{id}.
Store your secret securely. Never hardcode it in your application or commit it to version control. Use environment variables or a secrets manager.
Validating deliveries
import hashlib
import hmac
import os
from flask import Flask, request, abort
WEBHOOK_SECRET = os.environ["THEIRSTACK_WEBHOOK_SECRET"]
def verify_signature(payload: bytes, secret: str, signature_header: str) -> bool:
"""Verify the X-TheirStack-Signature-256 header."""
expected = "sha256=" + hmac.new(
secret.encode("utf-8"), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
app = Flask(__name__)
@app.route("/webhook", methods=["POST"])
def webhook():
signature = request.headers.get("X-TheirStack-Signature-256", "")
if not verify_signature(request.data, WEBHOOK_SECRET, signature):
abort(403)
event = request.json
# Process the event
return "", 200Always use a timing-safe comparison function like hmac.compare_digest. Never use == to compare signatures, as this is vulnerable to timing attacks.
Testing your implementation
You can verify your implementation with these test values:
| Field | Value |
|---|---|
| Secret | It's a Secret to Everybody |
| Payload | Hello, World! |
| Expected signature | sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17 |
Troubleshooting
- Signature mismatch — Make sure you are comparing against the raw request body (bytes), not a parsed/re-serialized JSON object. Re-serialization can change key order or whitespace.
- Missing header — The
X-TheirStack-Signature-256header is only included when a signing secret is configured for the webhook. - Encoding issues — The payload may contain unicode characters. Always handle it as UTF-8.
How is this guide?
Last updated on
Receive TheirStack events in your webhook endpoint
Learn the essential technical requirements for building webhook endpoints that receive TheirStack job and company data. Covers status codes, retry logic, concurrency handling, and duplicate prevention.
Company.New Webhook
Triggered when a new company matching your saved search criteria is discovered. [Learn more about webhooks](https://theirstack.com/en/docs/webhooks).
