Skip to main content
Technical Guides

Real-Time Intent Signals: How to Set Up Rodz Webhooks

Peter Cools · · 11 min read

In a nutshell: Webhooks let you receive Rodz intent signals the instant they fire, instead of polling the API every few minutes. This guide walks you through creating an endpoint, registering it with the Rodz API, verifying payloads with HMAC-SHA256, and handling retries. By the end, you will have a production-ready webhook pipeline delivering real-time signals straight into your stack.

What Are Webhooks and Why Do They Matter for Intent Signals?

A webhook is a server-to-server HTTP callback. When an event occurs on the Rodz platform, such as a new funding round detected, a key hire published, or a company relocation flagged, Rodz sends an HTTP POST request to a URL you control. Your server receives the event data in real time and can act on it immediately.

This is fundamentally different from polling. With polling, your application repeatedly calls the Rodz API at fixed intervals, asking “anything new?” Most of those requests return nothing. You waste compute, burn through rate limits, and still miss the moment a signal actually fires because you are always waiting for the next poll cycle.

Webhooks invert this dynamic. Rodz pushes data to you the moment it is available. No wasted calls, no delays, no guessing. For sales teams that depend on timing, the difference between learning about a funding round five minutes after it happens versus five hours later can determine whether you get the meeting or your competitor does.

If you have not yet set up your API credentials, start with the authentication guide before continuing here.

Prerequisites

Before you begin, make sure you have the following in place:

  1. A Rodz API key with webhook permissions. If you do not have one yet, follow the steps in Getting Started with the Rodz API to generate your credentials.
  2. At least one configured signal. Webhooks deliver signal events, so you need active signals. See Configure Your First Intent Signal for a walkthrough.
  3. A publicly accessible HTTPS endpoint. Rodz sends webhook payloads over HTTPS only. During development, tools like ngrok or Cloudflare Tunnel can expose a local server.
  4. cURL installed on your machine (or any HTTP client you prefer). All examples below use cURL.
  5. Basic familiarity with JSON and HTTP status codes.

Step-by-Step: Setting Up Rodz Webhooks

Step 1: Create Your Webhook Endpoint

Your endpoint is a route on your server that accepts POST requests and returns a 200 status code. Here is a minimal example using Node.js and Express:

const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/rodz', (req, res) => {
  const event = req.body;
  console.log('Received signal:', event.signal_type, event.company.name);

  // Process the signal (store in DB, notify sales team, trigger workflow)
  // ...

  res.status(200).json({ received: true });
});

app.listen(3000, () => console.log('Webhook server running on port 3000'));

The critical requirement: always return HTTP 200 (or 2xx) quickly. If your endpoint takes too long to respond or returns an error, Rodz will treat the delivery as failed and trigger retries. Do your heavy processing asynchronously, after you have acknowledged receipt.

Step 2: Register Your Webhook via the API

With your endpoint live, register it with Rodz using the /v1/webhooks endpoint. Replace YOUR_API_KEY with your actual key and update the URL to your endpoint:

curl -X POST https://api.rodz.io/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourdomain.com/webhooks/rodz",
    "events": ["signal.fired"],
    "description": "Production signal receiver"
  }'

A successful response returns the webhook object with its ID and a signing secret:

{
  "id": "wh_abc123def456",
  "url": "https://yourdomain.com/webhooks/rodz",
  "events": ["signal.fired"],
  "description": "Production signal receiver",
  "signing_secret": "whsec_k7G2mN9xPqR4...",
  "status": "active",
  "created_at": "2026-03-05T10:30:00Z"
}

Save the signing_secret immediately. It is only displayed once at creation time. You will need it to verify incoming payloads.

Step 3: Verify the Webhook (Handshake)

After registration, Rodz sends a verification request to your endpoint. This is a POST containing a challenge field:

{
  "type": "webhook.verification",
  "challenge": "ch_random_string_here"
}

Your endpoint must respond with a 200 status and echo the challenge value back:

app.post('/webhooks/rodz', (req, res) => {
  // Handle verification handshake
  if (req.body.type === 'webhook.verification') {
    return res.status(200).json({ challenge: req.body.challenge });
  }

  // Handle regular signal events
  // ...
  res.status(200).json({ received: true });
});

Once Rodz receives the correct challenge response, your webhook status moves from pending to active.

Step 4: Test the Webhook

Before relying on live signals, send a test event to confirm everything works end to end:

curl -X POST https://api.rodz.io/v1/webhooks/wh_abc123def456/test \
  -H "Authorization: Bearer YOUR_API_KEY"

Rodz will send a sample payload to your registered URL. Check your server logs to verify the test event arrived and was processed correctly. The test payload has the same structure as a real signal event but is flagged with "test": true so you can distinguish it from production data.

Step 5: Parse the Webhook Payload

Every signal event delivered by Rodz follows a consistent JSON structure. Here is what a real payload looks like:

{
  "id": "evt_789ghi012jkl",
  "type": "signal.fired",
  "test": false,
  "created_at": "2026-03-05T14:22:10Z",
  "webhook_id": "wh_abc123def456",
  "data": {
    "signal_id": "sig_345mno678pqr",
    "signal_type": "funding_round",
    "signal_name": "Series B Funding",
    "fired_at": "2026-03-05T14:22:08Z",
    "company": {
      "id": "comp_901stu234vwx",
      "name": "Acme Corp",
      "domain": "acmecorp.com",
      "country": "FR",
      "industry": "SaaS",
      "employee_count": 120
    },
    "details": {
      "amount": 15000000,
      "currency": "EUR",
      "round_type": "Series B",
      "investors": ["Index Ventures", "Partech"],
      "source_url": "https://example.com/article/acme-series-b"
    },
    "contacts": [
      {
        "name": "Marie Dupont",
        "title": "CTO",
        "email": "m.dupont@acmecorp.com",
        "linkedin_url": "https://linkedin.com/in/mariedupont"
      }
    ]
  }
}

Key fields to note:

  • id: Unique event identifier. Use this for idempotency (avoid processing the same event twice).
  • type: Always signal.fired for signal events.
  • data.signal_type: The category of signal (funding_round, key_hire, office_move, etc.).
  • data.company: Firmographic data about the company that triggered the signal.
  • data.details: Signal-specific information. The structure varies by signal type.
  • data.contacts: Relevant decision-makers at the company, when available.

Store the event id in your database after processing. If you receive the same id again (due to a retry), skip it. This idempotency check prevents duplicate outreach.

Step 6: Respond with 200

This bears repeating because it is the most common source of webhook issues: your endpoint must return a 2xx HTTP status code within 5 seconds. If Rodz does not receive a successful response in time, it considers the delivery failed.

Best practice: acknowledge immediately, process later.

app.post('/webhooks/rodz', (req, res) => {
  // Respond immediately
  res.status(200).json({ received: true });

  // Then process asynchronously
  processSignalEvent(req.body).catch((err) => {
    console.error('Failed to process webhook:', err);
  });
});

If you use a message queue (RabbitMQ, SQS, Redis Streams), push the raw payload onto the queue and return 200. A worker picks it up and handles the business logic at its own pace.

Webhook Payload Structure Reference

Here is a summary of the top-level fields present in every webhook delivery:

FieldTypeDescription
idstringUnique event ID for idempotency
typestringEvent type (signal.fired, webhook.verification)
testbooleantrue for test events, false for live signals
created_atstringISO 8601 timestamp of event creation
webhook_idstringID of the webhook that received this event
dataobjectSignal payload including company, details, and contacts

The data.details object varies by signal type. For a complete reference of all signal types and their detail schemas, consult the Rodz API Reference or the interactive API documentation.

Security: HMAC-SHA256 Signature Verification

Every webhook delivery from Rodz includes an X-Rodz-Signature header. This is an HMAC-SHA256 hash computed from the raw request body and your webhook’s signing secret. You should verify this signature on every incoming request to confirm the payload was sent by Rodz and was not tampered with in transit.

Here is how to verify in Node.js:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex');

  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

// In your route handler:
app.post('/webhooks/rodz', (req, res) => {
  const signature = req.headers['x-rodz-signature'];
  const rawBody = JSON.stringify(req.body);

  if (!verifySignature(rawBody, signature, process.env.RODZ_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Signature valid — process the event
  res.status(200).json({ received: true });
});

Important: Use crypto.timingSafeEqual instead of a simple === comparison to prevent timing attacks.

For a deep dive on signature verification patterns, including Python and Go examples, read our dedicated guide: Secure Your Webhooks with HMAC-SHA256 Verification.

Retry Policy

Rodz uses a retry mechanism with exponential backoff to ensure reliable delivery:

  • Attempt 1: Immediate delivery.
  • Attempt 2: 1 minute after the first failure.
  • Attempt 3: 5 minutes after the second failure.
  • Attempt 4: 30 minutes after the third failure (final retry).

A delivery is considered failed if your endpoint returns a non-2xx status code, times out (more than 5 seconds), or is unreachable.

After all 4 attempts fail, the event is marked as failed in the webhook delivery log. You can inspect failed deliveries and trigger manual re-delivery through the API:

# List recent deliveries for a webhook
curl https://api.rodz.io/v1/webhooks/wh_abc123def456/deliveries \
  -H "Authorization: Bearer YOUR_API_KEY"

# Retry a specific failed delivery
curl -X POST https://api.rodz.io/v1/webhooks/wh_abc123def456/deliveries/del_xyz789/retry \
  -H "Authorization: Bearer YOUR_API_KEY"

To avoid unnecessary retries, make sure your endpoint handles errors gracefully. Return 200 even if downstream processing fails (since you already received the data), and handle the processing failure through your own error tracking.

Managing Your Webhooks

Once registered, you can update, pause, or delete webhooks through the API.

List all webhooks:

curl https://api.rodz.io/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY"

Update a webhook (change URL or events):

curl -X PATCH https://api.rodz.io/v1/webhooks/wh_abc123def456 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://newdomain.com/webhooks/rodz",
    "events": ["signal.fired"]
  }'

Pause a webhook (stop deliveries without deleting):

curl -X PATCH https://api.rodz.io/v1/webhooks/wh_abc123def456 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "paused" }'

Delete a webhook:

curl -X DELETE https://api.rodz.io/v1/webhooks/wh_abc123def456 \
  -H "Authorization: Bearer YOUR_API_KEY"

For complete endpoint documentation, rate limits, and error codes, see the API Reference guide or the interactive API docs.

Frequently Asked Questions

What is the difference between webhooks and API polling?

Polling means your application repeatedly calls the API at intervals to check for new data. Webhooks reverse this: the server pushes data to you when something happens. Webhooks are more efficient (no wasted requests), faster (near-instant delivery), and friendlier to rate limits. For intent signals where timing matters, webhooks are the clear choice.

Can I register multiple webhooks for the same event?

Yes. You can register as many webhooks as your plan allows, and multiple webhooks can subscribe to the same event type. This is useful if you need to send signals to different systems, for example one webhook to your CRM integration and another to a Slack notification service.

What happens if my server is down when a signal fires?

Rodz will retry delivery up to 3 additional times using exponential backoff (1 minute, 5 minutes, then 30 minutes). If all attempts fail, the event is logged as a failed delivery. You can query the deliveries endpoint to find missed events and trigger manual retries once your server is back online.

How do I filter which signals trigger my webhook?

When registering a webhook, you subscribe to event types (like signal.fired). To control which specific signals are delivered, configure your signals through the Rodz API. Only signals you have actively configured will generate events. See Configure Your First Intent Signal for details on signal configuration.

Is HTTPS required for webhook endpoints?

Yes. Rodz only delivers webhooks to HTTPS URLs. This ensures that payload data, which may contain company information and contact details, is encrypted in transit. During local development, use a tunneling tool like ngrok to get a temporary HTTPS URL.

How can I debug webhook deliveries?

Start by checking the delivery log through the API. Each delivery record includes the HTTP status code returned by your endpoint, the timestamp, and whether it succeeded or failed. You can also use the test endpoint (POST /v1/webhooks/{id}/test) to send sample payloads on demand. For real-time debugging, inspect your server logs while triggering a test event.

What should I do if I lose my signing secret?

The signing secret is only shown once when you create the webhook. If you lose it, you need to delete the webhook and create a new one. The new webhook will receive a fresh signing secret. Make sure to store it securely in an environment variable or a secrets manager from the start.

Can I receive webhooks for signals across multiple workspaces?

Each webhook is scoped to the API key used to create it, which is tied to a specific workspace. If you operate multiple workspaces, register separate webhooks under each workspace’s API key. Your endpoint can handle payloads from all of them by checking the webhook_id field in each event.

Next Steps

You now have a fully functional webhook pipeline receiving real-time intent signals from Rodz. From here, you can:

  • Automate your outreach by connecting webhook events to your CRM or sequencing tool.
  • Build signal scoring by weighting different signal types and routing high-priority signals to your sales team immediately.
  • Secure your pipeline by implementing HMAC-SHA256 verification in production. Follow our detailed security guide.
  • Explore the full API in the Rodz API Reference or the interactive API documentation.

The faster you act on a signal, the higher your conversion rate. Webhooks make that speed possible.

Share:

Generate your outbound strategy for free

Our AI analyzes your company and creates a complete playbook: ICP, personas, email templates, call scripts.

Generate my strategy