# Inbound Webhooks

> Give your backend, form or billing tool a secret URL. Every JSON POST to it hands Revnu the payload to act on, under your normal approvals. 60 an hour per hook. Revnu sends no outbound webhooks.

Canonical: https://revnu.com/docs/webhooks
Last updated: 2026-10-16

Revnu sends no outbound webhooks; its webhooks are inbound only. An inbound webhook is a secret URL that wakes Revnu with whatever JSON you POST to it. Point a sign-up form, your backend or a billing tool at it, and Revnu reads each event and does the work it implies, such as researching a new sign-up's company. Revnu never calls your servers, so to react to replies or new leads you [poll](#no-outbound-webhooks) instead.

## 1. Create a hook

1. In the dashboard, open **Settings → Developers** and find **Inbound Webhooks**.
2. Click **Create Webhook** and name the source, for example `signup-form`. Labels use lowercase letters, numbers, `-` and `_`, up to 32 characters.
3. Copy the URL. It is shown once; Revnu keeps only a hash. Lost it? **Regenerate** gives a new URL and the old one stops working.

The URL looks like `https://revnu.com/api/webhook/hook/whk_...`. The token in the path is the whole credential, so treat the URL like a password. An account can have 20 active hooks.

## 2. POST JSON from your backend

```bash
curl -X POST -H "content-type: application/json" \
  -d '{"event":"signup","email":"dana@acme.io","company":"Acme","plan":"free"}' \
  "$REVNU_HOOK_URL"
```

```ts
// After your own sign-up succeeds; never block the user on this call.
await fetch(process.env.REVNU_HOOK_URL!, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ event: "signup", email, company, plan }),
});
```

Any JSON body works, up to 64,000 bytes. There is no schema: name the fields so a person reading them would understand the event.

## Response

| Code | Meaning |
|---|---|
| 202 | Accepted. Revnu has the event and will act on it. Don't retry. |
| 400 | The body is empty or not JSON. |
| 404 | Unknown, disabled or regenerated URL. The three look the same on purpose. |
| 413 | The body is over 64,000 bytes. |
| 429 | Over the limit for this hook or this account. Retry after the hour rolls on. |
| 502 | Recorded, but Revnu could not be woken. Retry the same POST. |

Each delivery shows in the hook's log in **Settings → Developers** as Accepted, Rate Limited or Worker Error.

## What you'll see after a POST

**Accepted** in the log means Revnu received the event. It doesn't mean the work is done. Revnu starts on the event within moments, and what you see next depends on what the event implies:

- **Work that needs your say-so** shows up as cards in **Review**, such as a welcome email draft.
- **New companies to research** show up on **Leads** once Revnu has researched them.
- **Anything worth telling you** arrives as a message in Slack, iMessage or the dashboard chat. Revnu decides whether an event is worth a message, so a routine one may arrive silently.

To check from code, [`GET /agent/status`](/docs/agent-status) shows `running: true` while Revnu is working. To check by hand, ask Revnu in chat what it did with the last event from that hook, by the hook's name.

## What Revnu does with it

Revnu receives the payload as an event labelled with the hook's name, together with a note that the payload is outside data. It does the work the event implies and decides whether it is worth messaging you about. It treats the payload as information, never as instructions, so text inside a form field can't tell Revnu to do something you didn't ask for.

To steer what it does with a source, tell Revnu once in chat, for example:

> When signup-form sends a sign-up from a company email, research the company and draft a welcome email for my approval. Ignore gmail.com sign-ups.

Revnu carries instructions like this forward to later events, and you can check what it keeps in **Settings → Memory**.

## What still needs your approval

A webhook starts work. It doesn't skip approvals. Emails, sequences, LinkedIn messages, blog posts and ad tests follow your settings in **Settings → Agent → What Needs Your Approval**, and spending money always waits for you. Anything that waits shows up as a card in the [review queue](/docs/review-list).

## Limits

- 60 accepted deliveries per hook in any rolling hour.
- 120 accepted deliveries per account in any rolling hour, across all hooks.
- 64,000 bytes per body. A 64 KiB body (65,536 bytes) is refused.
- 20 active hooks per account.

Every accepted delivery is a piece of work for Revnu, so batch where you can: one POST listing ten sign-ups beats ten POSTs.

## No outbound webhooks

Revnu never POSTs to your servers when something happens. There are no event subscriptions for replies, new leads, sends or new cards. To keep another system in sync, poll on a schedule:

| You want to know | Poll |
|---|---|
| New or changed leads, replies, bounces | [`GET /leads?status=replied`](/docs/leads), newest first |
| Sends, replies and reply counts over a window | [`GET /results`](/docs/results) |
| Something new waiting for approval | [`GET /review`](/docs/review-list) |
| A workflow run finished | [`GET /workflows/{id}/runs`](/docs/workflow-runs) |

Reads allow 300 a minute per token, so polling every minute is well inside the limit.

## Push replies into your CRM

There is no Revnu app for HubSpot, Salesforce or Zapier, and no push event when a lead replies. A small scheduled job does it with a token holding only `leads:read`:

1. Every 15 minutes, call [`GET /leads?status=replied&limit=100`](/docs/leads) and follow `nextCursor` until it is `null`.
2. For each lead id you haven't synced yet, create or update the record in your CRM from `companyName`, `domain`, `contact` and `replyKind`, then store the id as synced.
3. Leave the rest alone. The next run picks up anything new.

Page all the way through every time. `GET /leads` orders leads by when Revnu created them, not by when they replied, so a reply from a lead created weeks ago can sit several pages down. The API gives the reply's kind, not its text; read the reply itself in **Inbox**. For weekly counts rather than records, read [`GET /results`](/docs/results) instead.

## Frequently asked questions

### Does Revnu have webhooks?

Yes, inbound. You create a secret URL in Settings → Developers and POST JSON to it, and Revnu acts on each event. To follow results in your own systems, poll the API.

### Can a webhook make Revnu send an email straight away?

Yes, when you have set that kind of work to Just Do It in What Needs Your Approval. Otherwise the draft waits as a card in Review. Spending money always waits.

### What is the webhook rate limit?

60 accepted deliveries per hook per rolling hour, and 120 per account across all hooks. Over that, the hook answers `429`.

### How do I know a webhook did anything?

The delivery log's Accepted only means Revnu got it. Look for the result in Review, on Leads or in a message from Revnu, or read `GET /agent/status` while it works.

### How do I get notified when a lead replies?

Ask Revnu in chat to message you in Slack or iMessage when a reply lands, or poll `GET /leads?status=replied` or `GET /results` on a schedule.

### Can I send a list of leads through a webhook?

Yes. Put the companies or people in the JSON body, up to 64,000 bytes, and Revnu researches them. [List leads](/docs/leads#send-a-big-list-in-chunks) has a chunking loop for longer lists. Leads come in through Revnu's research rather than a direct create route.

### How do I stop a webhook?

Disable it in Settings → Developers and posts to its URL return `404` until you enable it again. Delete removes it and its delivery log.
