Webhooks
Set up a webhook
Build an endpoint, add it under Developers, copy the signing secret, create an API key for the pages and send a test event. Includes Clay, Zapier and n8n.
This guide takes you from nothing to a verified test delivery in your own endpoint. Allow 15 minutes.
You need the admin or manager role in your organization. Other members can see webhooks but not change them.
Build an endpoint
Add the webhook
Set filters
Copy the signing secret
Create an API key
Send a test event
Check Deliveries
Delivered, status 200.1. Build an endpoint
Your endpoint must do five things:
- Read the raw request body, before any JSON parsing.
- Verify the signature in
Signals-Signature. - Skip the request if you already handled its
webhookId. - Store the body and return
200within 10 seconds. - In a background job, read each event name's pages and store each event once, keyed by
eventId. See Read a run's events.
Store the signing secret in an environment variable named SIGNALS_WEBHOOK_SECRET, and the API key in SIGNALS_API_KEY. You get them in steps 4 and 5, so start with placeholders.
Use the raw body
If your framework parses JSON first and you re-serialize it, the bytes change and the signature fails. Read the raw bytes. See Common mistakes.
Deploy the endpoint on a public HTTPS URL, for example https://your-app.com/signals-hook. Signals rejects URLs that do not meet these rules:
| Rule | Reason |
|---|---|
Starts with https:// | Plain http:// is not accepted. |
| Port 443 | Custom ports such as :8443 are rejected. |
| A public hostname | localhost, *.local and *.internal are rejected. |
| No IP address | https://203.0.113.7/… is rejected. |
| No wildcard DNS | nip.io, sslip.io and similar are rejected. |
| No username or password | https://user:pass@host/… is rejected. |
| Final URL | Redirects are never followed. |
To test from your laptop, use a tunnel service that gives you a public https hostname on port 443.
2. Add the webhook
Open Developers in the sidebar. The page has API keys and Webhooks.
In Webhooks, click Add webhook. Enter the Endpoint URL.
3. Set filters
Under the URL you see the filters. The dialog says: "An empty filter sends every value."
- Events: families and sub-types. Default All events.
- Actions:
new,updated,removed. Default all three. - Competitors: searchable list. Default All competitors.
- Projects: searchable list. Default All projects.
Filters combine with AND. They decide the counts in the POST and the events in the pages. You can change them later with Edit. See How webhooks work.
Click Add webhook.
4. Copy the signing secret
After you save, Signals shows the Signing secret. It starts with whsec_. The panel says: "You won't see this secret again. Store it now."
Click Copy secret, save it in your app as SIGNALS_WEBHOOK_SECRET, then click Done. If you lose it, use Rotate secret.
Treat the secret like a password. Do not commit it to git. Anyone with the secret can forge requests to your endpoint.
5. Create an API key
The POST has no events in it. Your endpoint reads them with GET /api/public/v1/events?pageToken=…, which needs an API key of the same organization.
In Developers → API keys, click Create key, name it after your endpoint, and store it as SIGNALS_API_KEY. See Create an API key.
6. Send a test event
Open the row menu (Webhook actions) and choose Send test event.
Signals queues a test run. It arrives within a minute. The test:
- is a normal signed run webhook, in the same shape as real ones, with
reviews.newattotal: 1; - has a
firstPageTokenthat returns one sample G2 review, so you can test your page loop too; - carries the header
Signals-Test: true; - is sent only to an active webhook, and only one test can wait at a time.
Use Signals-Test to skip test deliveries in your business logic.
7. Check Deliveries
Choose View deliveries from the row menu. A successful test reads Delivered, with the status code 200, the time in ms and Attempt 1 of 9.
If the row is Failed or Dead, look at its error label and fix the cause. See Retries and failures.
Real runs now arrive as trackers finish their checks, one POST per run.
No-code recipes
Zapier, Clay and n8n receive the POST like any other client. Most of them cannot verify the HMAC signature, because they do not expose the raw body.
Without signature checks
Keep the URL secret (it is the only protection), filter by body keys, and rotate the URL if it leaks. Whenever you can run a few lines of code, verify the signature instead.
In each tool, the POST only tells you that a run has events. Add an HTTP step that calls GET /api/public/v1/events?pageToken=… with the x-api-key header, then loop over items. Most runs fit in one page (totalPages: 1). For more pages, repeat the HTTP step with nextPageToken until it is null.
Clay
Create a webhook table
https URL on the standard port.Add it in Signals
Read the page
events token for the name you want, for example events["jobs.new"].firstPageToken. Write each item of items to its own table, and map competitor.name, signalType and metadata to columns.Zapier
Create a Zap with Catch Hook
Add it in Signals
Fetch the page
x-api-key header. Then add a Looping by Zapier step on items to run the Zap once per event.n8n
Add a Webhook node
POST. Use the Production URL, not the test URL, once the workflow is active.Respond fast
200 within 10 seconds.Page through the events
x-api-key header, and turn on its pagination: next URL from nextPageToken, stop when it is empty. Add a Split Out node on items, then an IF node on {{ $json.signalType }}.