WebhooksHow to set up a webhookMonitor your webhooksReceive TheirStack events in your webhook endpointVerify webhook signatures

Event types

Company.New WebhookJob.New Webhook
TheirStackTheirStack Logo
Log inSign up
DocumentationAPI ReferenceWebhooksDatasetsMCPGuides

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

  1. You set a signing secret (minimum 16 characters) when creating or editing a webhook.
  2. 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-256 header:
X-TheirStack-Signature-256: sha256=<hex_digest>
  1. 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 secret field 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 "", 200

Always 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:

FieldValue
SecretIt's a Secret to Everybody
PayloadHello, World!
Expected signaturesha256=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-256 header 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).

On this page

How it works
Setting up a signing secret
Validating deliveries
Testing your implementation
Troubleshooting