# How do you send funding rounds detected by Rodz to an n8n workflow?

> Source: https://www.rodz.io/en/guides/use-cases/funding-rounds-to-n8n/
> Updated on 2026-10-12 · Peter Cools · Recipe · Funding rounds · n8n

## Short answer

Create a Fundraising signal in Rodz and paste the URL of an n8n Webhook node as its webhookUrl. Every round detected arrives as a POST with the amount, stage, investors and company. A Code node verifies the HMAC signature, an IF node filters by amount, then n8n routes it to your CRM or Slack. There is no native Rodz node: everything goes through the webhook. A lead costs 2 credits.

## Key facts

- A funding round costs **2 credits** per lead, plus 0.5 credit per email found and 5 credits per phone found, charged on success only.
- Every delivery is signed with **HMAC-SHA256** (`X-Webhook-Signature`, `X-Webhook-Timestamp`) and carries an `X-Webhook-Id` that stays the same across retries.
- A delivery that fails transiently (timeout or `5xx`) is **retried** with exponential backoff, so the same round can arrive twice.
- Rodz identifies **400,000+ signals a month** (July to September 2026 average).

Cost: 2 credits per lead (+0.5 credit per email found)

## Before you start

- A Rodz account with credits (the test account includes 100) and an API key (Integrations → API), which carries the webhook secret.
- An n8n instance reachable from the internet (n8n Cloud, or self-hosted over HTTPS).
- Self-hosted: the environment variable `NODE_FUNCTION_ALLOW_BUILTIN=crypto`, so the Code node can load the `crypto` module.
- Your criteria: round types (`seed`, `series_a`...), minimum amount, headquarters country, industries.

## Steps

### 1. Create the Webhook node in n8n

In a new workflow, add a **Webhook** node: method **POST**, respond **Immediately** (Rodz expects a fast `2xx`). In the options, turn on **Raw Body**: the signature is computed over the exact bytes received, not over re-serialized JSON.

Copy the node’s production URL (not the test URL, which only answers while you listen manually).

### 2. Create the Fundraising signal with that URL

In Rodz, add the **Fund Raising** signal and paste the n8n URL in the webhook field, or create it through the API. To receive a decision-maker at the funded company (not only the company), turn on persona targeting: without it, `contact` is `""`.

Keep the signal as a **draft** while you test.

`Create it through the API (draft status)`

```bash
curl -X POST "https://api.rodz.io/api/v1/signals/fundraising" \
  -H "Authorization: Bearer $RODZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Series A+ in France",
    "webhookUrl": "https://n8n.example.com/webhook/rodz-funding",
    "dailyLeadLimit": 50,
    "status": "draft",
    "config": {
      "investmentTypes": ["series_a", "series_b"],
      "amountMin": 2000000,
      "announcedPeriod": "30d",
      "hqLocations": ["France"],
      "enablePersonaTargeting": true,
      "targetPersonas": ["CEO", "Finance Director"],
      "getEmails": true
    }
  }'
```

### 3. Verify the signature in a Code node

Add a **Code** node (JavaScript, *Run Once for All Items*) after the Webhook. It recomputes the HMAC-SHA256 of `timestamp + "." + raw body` with your webhook secret (Integrations → API in Rodz), compares it in constant time and rejects stale messages. Keep the secret in an n8n environment variable, not in the code.

`n8n Code node: verify-rodz-signature.js`

```javascript
const crypto = require('crypto');

const SECRET = $env.RODZ_WEBHOOK_SECRET; // Integrations → API in Rodz
const TOLERANCE_SECONDS = 300;

const item = $input.first();
const headers = item.json.headers || {};
const signature = headers['x-webhook-signature'] || '';
const timestamp = headers['x-webhook-timestamp'] || '';

// Body exactly as received (Webhook node, Raw Body option).
const raw = await this.helpers.getBinaryDataBuffer(0, 'data');

const expected = crypto
  .createHmac('sha256', SECRET)
  .update(Buffer.concat([Buffer.from(`${timestamp}.`, 'utf8'), raw]))
  .digest('hex');

const a = Buffer.from(expected, 'utf8');
const b = Buffer.from(signature, 'utf8');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
  throw new Error('Rodz webhook: invalid signature');
}
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) {
  throw new Error('Rodz webhook: timestamp outside tolerance');
}

const payload = JSON.parse(raw.toString('utf8'));
return [{ json: { webhookId: headers['x-webhook-id'], ...payload } }];
```

### 4. Filter and route the round

Add an **IF** node on `{{ $json.signal.funding_amount_usd }}` (for example greater than or equal to 5,000,000) and drop test messages (`signal.is_test` true). Then wire the node of your choice: your CRM, a Slack message with `signal.article_summary` and `signal.article_use_of_funds`, or a spreadsheet row.

Retries reuse the same `X-Webhook-Id`: before writing to the CRM, skip a `webhookId` you already processed (for example with the **Remove Duplicates** node on that field, or a lookup in your database).

### 5. Test, then activate

Activate the n8n workflow, then send a test lead from Rodz: `POST /api/v1/signals/{signal_id}/test-webhook`. The response gives the status code n8n returned and `signed`: `false` means no API key is linked to the signal, so deliveries go out unsigned and the Code node will reject them. When everything is green, set the signal to **Active**.

`Send a test payload`

```bash
curl -X POST "https://api.rodz.io/api/v1/signals/$SIGNAL_ID/test-webhook" \
  -H "Authorization: Bearer $RODZ_API_KEY"
```

## Example payload

What the Webhook node receives for one round, with persona targeting on (without it, `contact` is `""`).

```json
{
  "signal": {
    "id": "d4f5a6b7-c8d9-4e0f-a1b2-c3d4e5f6a7b8",
    "name": "Series A+ in France",
    "type": "fundraising",
    "last_signal": "Fundraising",
    "last_signal_date": "2026-10-09",
    "funding_type": "venture",
    "funding_stage": "Series A",
    "funding_amount": 6000000,
    "funding_amount_usd": 6500000,
    "funding_currency": "EUR",
    "investors": ["Example Ventures Fund", "Angel Investor"],
    "num_investors": 2,
    "funding_total_usd": 8200000,
    "announcement_date": "2026-10-08",
    "article_summary": "Northwind Robotics raised a EUR 6M Series A to expand across Europe.",
    "article_use_of_funds": "Hiring 20 engineers and opening Germany.",
    "article_source_url": "https://news.example.com/northwind-robotics-series-a"
  },
  "contact": {
    "lead_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "created_at": "2026-10-09T08:05:00+00:00",
    "first_name": "Claire",
    "last_name": "Martin",
    "email": "claire.martin@northwind-robotics.example",
    "phone": "",
    "title": "CEO",
    "location": "Nantes, Pays de la Loire, France",
    "country_code": "FR",
    "city": "Nantes",
    "linkedin_profile_url": "https://www.linkedin.com/in/claire-martin-example",
    "sales_navigator_profile_url": "",
    "matched_persona": "CEO"
  },
  "company": {
    "name": "Northwind Robotics",
    "website": "https://northwind-robotics.example",
    "linkedin_url": "https://www.linkedin.com/company/northwind-robotics-example",
    "industry": "Automation Machinery Manufacturing",
    "hq_country_code": "FR",
    "hq_city": "Nantes",
    "employee_range": "51-200"
  }
}
```

## The fields that matter for routing a round

| Field                         | Content                                    | Use in n8n                            |
| ----------------------------- | ------------------------------------------ | ------------------------------------- |
| `signal.funding_stage`        | Round stage (Seed, Series A...)            | IF branches per stage                 |
| `signal.funding_amount_usd`   | Amount in US dollars                       | Priority threshold                    |
| `signal.investors`            | List of investors                          | Mention in the Slack message          |
| `signal.announcement_date`    | Announcement date                          | Freshness of the round                |
| `signal.article_summary`      | Summary of the announcement                | Slack message or CRM note text        |
| `signal.article_use_of_funds` | Planned use of the money                   | Outreach angle (hiring, expansion...) |
| `contact.*`                   | Targeted decision-maker (with personas on) | Contact creation                      |
| `company.website`             | Company website                            | Account deduplication key             |

## A typical workflow

1. **Webhook** (POST, Raw Body, respond immediately).
2. **Code**: signature check, then the lead JSON.
3. **IF**: `signal.is_test` true → stop; `funding_amount_usd` below the threshold → a spreadsheet row only.
4. **Remove Duplicates** on `webhookId`.
5. **CRM**: look up the account by domain, create or update it, then the contact.
6. **Slack**: a message to the territory rep with the summary and the use of funds.

If you need no custom logic, HubSpot and lemlist are native Rodz destinations: see [funding rounds to HubSpot](/en/guides/use-cases/funding-rounds-to-hubspot/) and [funding rounds to lemlist](/en/guides/use-cases/funding-rounds-to-lemlist/).

## Troubleshooting

- **The Code node rejects everything**: check that Raw Body is on, that the secret belongs to the API key linked to the signal, and that the test reports `signed: true`.
- **`require('crypto')` is refused**: self-hosted, add `NODE_FUNCTION_ALLOW_BUILTIN=crypto` to n8n’s environment.
- **Nothing arrives**: the workflow must be active and the URL must be the Webhook node’s production URL.

## Frequently asked questions

### Is there a native Rodz node in n8n?

No. The integration goes through the signal’s webhook: Rodz POSTs every lead to your Webhook node URL. To read or create signals from n8n, use the HTTP Request node with the Rodz API and your key in an `Authorization: Bearer` header.

### Why turn on Raw Body in the Webhook node?

The HMAC signature covers the exact bytes of the body received. If you recompute it over JSON that was parsed and re-serialized, key order or whitespace changes and the check fails.

### What happens if n8n is down?

A timeout or a `5xx` response triggers retries with exponential backoff. A `4xx` response is treated as permanent and is not retried. After a long outage, `POST /api/v1/signals/{signal_id}/replay-webhook` resends every lead of the signal (`signal.is_replay` true).

### How do I avoid duplicates in my CRM?

Deduplicate on the `X-Webhook-Id` header, which is the same on every attempt of one delivery. On the Rodz side, the signal’s duplicate handling also stops the same company from being delivered twice.

### Which currency are the amounts in?

`funding_amount` is in the announced currency (`funding_currency`) and `funding_amount_usd` in US dollars. Filter on `funding_amount_usd` to compare rounds from different countries; the signal’s `amountMin` filter is in USD too.

## Sources

- [Rodz API reference: Fundraising signal, webhooks, signature and retries](https://api.rodz.io/docs)
- [n8n: Webhook node (Raw Body option)](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/)
- [n8n: Code node](https://docs.n8n.io/code/code-node/)
- [RFC 2104: HMAC](https://www.rfc-editor.org/rfc/rfc2104)

## Related guides

- https://www.rodz.io/en/guides/api/webhook-receiver-hmac/
- https://www.rodz.io/en/guides/use-cases/funding-rounds-to-hubspot/
- https://www.rodz.io/en/guides/use-cases/funding-rounds-to-lemlist/
