# List webhooks: receive records from forms and tools

Source: https://listplus.ai/en/help/lists-and-import/list-webhooks
Last updated: 2026-10-03

Every list can have its own **webhook**: a private URL that a form, Zapier,
Make, n8n or your own code sends records to. Every record lands in the list,
in the matching columns, and the list's own checks show what is wrong with
it. If you like, doubtful records wait in the **Inbox** for your decision
first — that is an option per webhook, off by default. It is free on every
plan and uses no credits.

A list webhook is for structured records (JSON) that belong in one
particular list. Forwarded emails, PDFs and pasted text still go to the
Inbox itself — see "Sending into the Inbox from n8n, Zapier, Shortcuts,
scripts and email".

## Creating the webhook

A list has at most one webhook. Anyone who can edit the list can create it,
in any of three places:

- **In the list:** click the **Set up** icon next to the list's name (the two sliders; the **⋯** menu → **List Settings...** opens the same page) → **Webhook**
  → **Create webhook**.
- **On a list without records:** under "How do you want to fill this
  list?", click the **Receive from a tool** card (**Get the webhook URL**).
  It opens Set up on the Webhook section.
- **In the Inbox:** switch to **Entries**, then (with no entry selected) click **Webhook for a list**,
  choose the list and click **Create webhook**.

You then see the URL with a **Copy** button, the status (**Active** or
**Paused**) and a counter such as "Today 14 / 100". Treat the URL like a
password: anyone who has it can send records to this list.

A reference list that refreshes automatically from its source can't have a
webhook — it takes its records from the source only ("This list is
refreshed from its source and takes no data here.").

## What to send

Send a **POST** request with JSON: one object per record, or an array of
objects for several records at once. The **How to set up your tool**
section of the panel has a tab for each case:

- **Fields** — a JSON template built from this list's own columns. With
  these field names every value lands in its column right away.
- **curl** — the same body as a ready command for a terminal.
- **n8n** — add an "HTTP Request" node, choose "Import cURL" and paste the
  command from the curl tab.
- **Zapier · Make** — Zapier: the "Webhooks by Zapier" action with "POST";
  Make: the "HTTP" module with "Make a request". Paste the URL, choose JSON
  and add the fields from the Fields tab.
- **Typeform · Calendly** — paste the URL as a webhook in Typeform
  ("Connect" → "Webhooks"), or point a Calendly webhook for
  "invitee.created" at it. These tools send their own format; ListPlus
  reads the answers, and the question's title becomes the field name.

Click **Send test** to check the setup: it sends the template as a dry run
and tells you how many fields would be placed. Nothing is written to the
list.

## How fields find their columns

There is no mapping step. The list knows what its columns are (email, first
name, company …), so common field names in English, German, French and
Spanish are placed on their own — `email`, `E-Mail`, `first_name`,
`Vorname`, `company`. A field name ListPlus has never seen is looked at by
AI once per webhook, and the answer is remembered, so the same form never
asks twice. A full-name field fills the first-name and last-name columns
when the list has no full-name column.

A field without a matching column is **not lost**: its value stays with the
record. After the first call, the **Mapping** section lists every field
that arrived, with an example value and the column it goes to. There you
can pick another column, choose **No column**, or pick **+ Create as
column** to add a column named like the field. A change applies to the
records that arrive from then on.

## What happens to a record

By default **every record goes into the list**. The webhook doesn't judge
your leads: the list's own checks in **Analyze** flag an invalid email, a
placeholder or a test entry like in any other list, and an evaluation
shows which records are ready, enrichable or unusable (see "What the
Analyze panel checks and how the quality score works").

- **New records** are added. Triggers on the list act on them like on any
  new record, so webhook → list → trigger → pipeline works.
- **Duplicates** are merged: a record that matches an existing one by
  email, LinkedIn URL, phone or name + company fills that record's empty
  fields instead of adding a second row.

Two things are **always held back** in the Inbox instead, whatever you set:

- records that match your workspace's **Block list** ("On the block list");
- records the list can't take: "Plan's record limit reached" (the
  workspace is over its plan's record limit — see "[Record limit](https://listplus.ai/en/help/plans-credits-billing/record-limit)"), "List
  takes no data" or "Could not be written".

## Hold back (optional)

The **Hold back** section of the panel has two checkboxes, both off at
first. What you tick waits in your Inbox for your decision instead of
going into the list; a change is saved at once and applies to the records
that arrive from then on.

- **Hold back invalid, disposable and test addresses** — simple rules on
  the email address: not a valid address, a throwaway provider, or a test
  domain. No AI involved.
- **Hold back spam and test entries (AI check)** — records with nothing
  that says who it is (no name, email, company, phone or LinkedIn),
  placeholder values, and what an AI check clearly judges to be a test
  entry, keyboard mashing or spam. When the AI is unsure, the record goes
  through. This is the only setting that sends record values to an AI —
  see "[What the AI sees of your data](https://listplus.ai/en/help/working-in-a-list/what-the-ai-sees)".

Leave both off if a trigger or your own checks sort the records anyway;
tick them if every record in the list should be worth a look, for example
when a public form feeds a list that hands records straight to your CRM.

## Deciding held-back records in the Inbox

Held-back records appear in the Inbox as an entry named after the list,
marked "n to review", in the **All** and **To review** tabs. A very large
batch is spread over several entries of up to 200 records each. Open it to see
each record with its **Reason** — "On the block list" or "Plan's record
limit reached", and with a Hold back option on also "Invalid email",
"Disposable address", "Test address", "No name, no email", "Placeholder"
or "Looks like a test or spam". Then click **Add anyway** or **Discard**. The list is already set,
so there is nothing to choose. Tick single rows to decide only those;
without a tick the buttons act on all open records.

**Add anyway** puts the records into the list without checking them again;
a duplicate is still merged. Like every Inbox entry, an entry with
held-back records is deleted **14 days** after it arrived, so decide within
that time. The Inbox keeps up to 500 live entries; if it is full, held-back
records can't be stored and the panel's log says "Inbox full – rows
dropped".

Once at least one list has a webhook, the Inbox shows a small tree on the
left: **Inbox** on top, and under **Lists** every list with a webhook, with
the number of records waiting. Click a list there to see its entries and
its webhook page (the same panel as under Set up → Webhook, plus **Open list**).
**Webhook for a list** at the bottom of the tree adds another one.

## When the same person comes in again

A record that matches an existing one only fills that record's empty
fields. Under **When the same person comes in again** in the webhook
section you can set what a repeated entry changes beyond that — useful for
sources that report every visit or every event:

- **Overwrite these columns with the newer value** — for example Last seen
  and Last page.
- **Count in this column how often the person came in** — a new record
  starts at 1, every further entry adds 1.

A value that arrives as a moment in time (for example
`2026-10-02T09:15:00Z`) is stored as its day in a date column.

Triggers that react to matching records run again when such a column
changes — for example "Last page contains /pricing".

## RB2B: identified website visitors

RB2B sends a fixed set of fields per visitor. Under **How to set up your
tool**, choose **RB2B** and click **Set up for RB2B**: ListPlus shows which
columns it will add (it uses the ones the list already has — e-mail, name,
company and so on — and adds the rest, including **Last seen**, **Last
page**, **Referrer** and **Visits**), places RB2B's fields and sets the
rule for repeated visits. No mapping, no AI question.

Then, in RB2B:

1. Open **Integrations → Webhook**, paste the list's webhook URL and save.
2. Switch on **Send repeat visitor data** if you want Last seen, Last page
   and Visits kept up to date. Leave **Sync company-only profiles** off —
   the list holds people, and a visitor without a person has nothing to be
   matched by, so every such visit would add a row.
3. Click **Send a Test Event**. The test visitor appears in the list.

RB2B often identifies a visitor by LinkedIn profile without an e-mail
address. Such records show as enrichable in the list's checks — enrich only
the ones that fit your audience. RB2B does not resend an event that was
missed, and on Basic the daily limit is 100 records.

## Limits

| Plan    | Records per day | Records per call | Calls per minute |
| ------- | --------------- | ---------------- | ---------------- |
| Basic   | 100             | 100              | 30               |
| Pro     | 5,000           | 500              | 60               |
| Premium | 50,000          | 500              | 120              |

The daily number counts all list webhooks of a workspace together; calls
per minute count per webhook. The workspace owner's plan decides. Over a
limit the sender gets an error (HTTP 429) instead of the record being
dropped silently, so tools like Zapier or n8n can try again; a call that
hit the daily limit also shows in the panel as "refused – daily limit
reached".

## Pause, new URL, delete

The **⋯** menu next to the counter offers:

- **Pause** / **Resume** — a paused webhook refuses calls; nothing is
  stored while it is paused.
- **Create a new URL** — the current URL stops working right away; mapping
  and log are kept. Use it if the URL may have leaked.
- **Delete webhook** — the URL stops working, mapping and log are deleted.
  The records in the list stay.

Under **Recent calls** you see what the latest calls did, for example "3
received · 1 new · 2 held back".

## For advanced users

- **Request:** `POST` with `Content-Type: application/json`. A form post
  (`application/x-www-form-urlencoded`) works too. A `GET` on the URL
  returns a description: the list's fields, an example body, the limits
  and the response codes.
- **Body shapes:** an object, an array of objects, or an object that wraps
  an array under `rows`, `records`, `items`, `leads`, `contacts` or `data`.
  Nested objects are flattened: `company.name` becomes `company_name`,
  while a mere wrapper such as `contact.email` becomes `email`. Arrays of
  question/answer pairs become fields.
- **Sizes:** body up to 1 MB, up to 100 fields per record, up to 2,000
  characters per value.
- **Responses:** `202` with `{ "success": true, "received": n }` — the
  answer comes at once, and placing, checking and writing happen right
  after it. `400` no JSON object or array of objects, `401` invalid or
  replaced URL, `409` webhook paused or the list takes no data, `413` too
  many records in one call or body too large, `429` limit reached (per
  minute or per day).
- **Dry run:** add `?test=1` to the URL. The answer shows where each field
  would go and which names are not placed yet; nothing is written or
  counted.
- **Where a call came from:** add `?source=…` to the URL (for example
  `?source=partner-acme` or `?source=booth-qr`) to tell several senders of
  one webhook apart. `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`
  and `utm_content` work the same way. The value is kept with every record
  of the call like any other field: in a column of that name if the list
  has one, otherwise with the record. A field of the same name in the
  record itself wins. Values are cut at 200 characters.
- **If something is busy for a moment:** an accepted call is kept until its
  records are stored. If the list is busy with another change, the call is
  tried again (up to three times, about 30 seconds apart) before its
  records are held in the inbox as "Could not be written".
- **Daily counter:** runs on UTC days. A call that would pass the daily
  limit is refused as a whole.
- **What the AI gets:** with both Hold back boxes off and field names the
  list knows, a call uses no AI at all. An unknown field name is sent once,
  with one example value (up to 80 characters) and the list's column names
  and types. With "Hold back spam and test entries" on, each record's
  values (up to 12 fields, 120 characters each) go to the AI check. Details
  in "[What the AI sees of your data](https://listplus.ai/en/help/working-in-a-list/what-the-ai-sees)".
- **Log:** the panel shows the latest 8 calls; 50 are kept.
- There is no signature header; the secret is the URL.

**Related topics:** send-to-inbox-from-tools · inbox · zapier-make-n8n ·
list-triggers · record-limit · what-the-ai-sees · quality-checks-overview.

## FAQ

### What is a list webhook?

A private URL that belongs to one list: a form, Zapier, Make, n8n or your own code sends records to it as JSON, and they land in that list's matching columns. By default every record goes into the list and the list's checks show what is wrong with it; if you like, doubtful records wait in the Inbox for your decision first. Each list can have one webhook, on every plan.

### How do I create a webhook for a list?

In the list, click the Set up icon next to the list's name, open the Webhook section and click Create webhook, then copy the URL. On a list without records, the card "Receive from a tool" opens the same section, and in the Inbox you can switch to Entries and click Webhook for a list (with no entry selected) and choose the list; anyone who can edit the list can do this.

### What format does a list webhook expect?

A POST request with JSON: one object per record, or an array of objects for several records in one call. The Fields tab under "How to set up your tool" shows a ready JSON template built from the list's own columns, and the curl tab the same as a command. Typeform and Calendly webhooks and plain form posts are understood too.

### Do I have to map the webhook's fields to my columns?

No. Fields find their column by the list's column types (email, first_name, company …), and a name ListPlus doesn't know is looked at by AI once and then remembered. Fields without a column are kept with the record; in the Mapping section you can assign them to a column or pick "+ Create as column", which applies to records arriving from then on.

### How do I connect Zapier, Make, n8n, Typeform or Calendly to a list webhook?

Zapier: the "Webhooks by Zapier" action with POST; Make: the HTTP module with "Make a request"; n8n: an HTTP Request node with "Import cURL" and the command from the panel's curl tab. Typeform ("Connect" → "Webhooks") and Calendly (a webhook for "invitee.created") can call the URL directly, and the question's title becomes the field name. The panel's "How to set up your tool" tabs show these steps next to your URL.

### Does a list webhook block bad leads, spam or test entries?

Not by default: every record goes into the list, and the list's checks in Analyze flag invalid emails, placeholders and test entries. To keep them out, tick "Hold back invalid, disposable and test addresses" and/or "Hold back spam and test entries (AI check)" in the Hold back section of the webhook panel (Set up → Webhook); those records then wait in your Inbox for Add anyway or Discard. Contacts on your workspace's Block list are always held back, whatever you tick.

### Why did a record sent to my list webhook not appear in the list?

Look in the Inbox for an entry named after the list, marked "to review": the record was held back because it is on your Block list, your plan's record limit is reached, or you ticked a Hold back option that caught it. It may also have been merged into an existing record as a duplicate (the Recent calls log says "merged"), or the call was refused — a paused webhook, an old URL or a limit all return an error to the sender. Recent calls in the webhook panel shows what each call did.

### Where do held-back webhook records go, and how do I add them?

Records on your Block list, records the list can't take and whatever you chose to hold back wait in the Inbox as an entry named after the list, marked "n to review" and shown in the To review tab. Open it to see each record with its reason, then click Add anyway or Discard — the list is already set, and you can tick single rows to decide only those. The entry is deleted 14 days after it arrived, like every Inbox entry, so decide before then.

### Does a list webhook create duplicates?

No. A record that matches an existing one in the list by email, LinkedIn URL, phone or name + company is merged: it fills that record's empty fields instead of adding a second row. The "Recent calls" log shows this as "merged".

### How do I send RB2B visitors into a ListPlus list?

Open the list's Set up page → Webhook, choose RB2B under "How to set up your tool" and click "Set up for RB2B": ListPlus adds the columns RB2B's fields need (using the ones the list already has), places the fields and sets the rule for repeated visits. Then paste the webhook URL in RB2B under Integrations → Webhook and click "Send a Test Event". No field mapping is needed.

### Can a list webhook update a contact that comes in again, for example on a repeat visit?

Yes. By default a repeated record only fills the existing record's empty fields; under "When the same person comes in again" you choose columns that take the newer value (for example Last seen and Last page) and a column that counts the visits. "Set up for RB2B" sets this for you.

### Does a list webhook cost credits, and what are its limits?

It is free on every plan and uses no credits — only enriching the records afterwards costs. The plans differ in the limits: Basic 100 records a day (100 per call, 30 calls a minute), Pro 5,000 (500 per call, 60 a minute), Premium 50,000 (500 per call, 120 a minute), counted for all list webhooks of the workspace and shown as "Today 14 / 100". Over a limit the sender gets an HTTP 429 error so the tool can retry; nothing is dropped silently.

### How do I pause a list webhook, get a new URL or delete it?

Open the ⋯ menu next to the "Today" counter under Set up → Webhook: Pause stops it taking calls until you click Resume, and nothing is stored while it is paused. "Create a new URL" makes the old URL stop working at once and keeps mapping and log — use it if the URL leaked. "Delete webhook" removes URL, mapping and log; the records in the list stay.

### How do I test a list webhook and see what arrived?

Click Send test in the panel: it sends the list's field template as a dry run and reports how many fields would be placed, without writing anything to the list. After real calls, "Recent calls" shows per call how many records were received, new, merged and held back. Before the first call the Mapping section says "Waiting for the first call …".

### Do triggers and pipelines run for records that arrive by webhook?

Yes. A record the webhook adds to the list is a new record like any other, so the list's triggers check it and can hand it to a pipeline, set a status or tag it. Since the webhook lets every record through by default, give the trigger conditions or tick a Hold back option if only good records should be handed on; held-back records reach the list, and its triggers, only after you click Add anyway.
