What Is a Webhook vs API? Pull, Push, and the Moment a Prospect Opens Your Link

HTMLvault Team·October 13, 2026·10 min read

Most answers to “what is a webhook vs API” stop at the definitions. This one follows a single job from start to finish. You sent each contact on a deal their own link to a proposal, and you want to know the moment one of them opens it.

At Synergetics, Chip Bellfort handles this by sending at 11:47 p.m. and refreshing the link's analytics until about one in the morning. That is polling, performed by a person pushing a button.

Software can poll too, and for some jobs it should. This job has a better fit: HTMLvault sends a recipient.viewed event to your URL when the view happens. Below you'll find both approaches in plain terms, a side-by-side table, the event documented end to end, and its limits.

What is a webhook vs API, in plain terms

An API (application programming interface) is a set of addresses a program calls to read data or make a change. HTMLvault's REST API lets a script create links, update them and read their analytics. With an API, your code starts every exchange. To learn whether a prospect opened their link, your script asks, gets an answer, waits, and asks again. That loop is called polling.

A webhook works the other way round. You give HTMLvault a URL, and when a specific event occurs, HTMLvault sends an HTTP POST to that URL describing it. Nobody asks. The message was pushed because the event was triggered.

In proposal terms, polling is refreshing your email every fifteen minutes and a webhook is a notification. The difference is a delay of up to one polling interval, plus a bunch of clicks that come back empty.

Refreshing by hand every 15 minutes compared with a browser or SMS notification for one 9:32 open Refreshing by hand vs getting a notification Refreshing by hand POLLING Getting notified WEBHOOK 9:00 Refresh No new views 9:15 Refresh No new views 9:30 Refresh No new views 9:45 Refresh Opened at 9:32 BROWSER · 9:32 Your proposal was just opened SMS · 9:32 A contact opened their proposal 3 EMPTY REFRESHES · 13 MIN LATE 0 REFRESHES · ARRIVES AT 9:32
Webhook vs API polling in everyday terms: a refresh habit pays for every open in empty checks and lag, while a webhook lets the open itself trigger the alert, so the speed no longer depends on how often anyone looks.

Webhook vs API side by side

The table runs the proposal example through both approaches: a script that checks 30 proposal links every 15 minutes, and a webhook on the same links. Each row asks one question and answers it for both, and the bottom row is the takeaway: poll the API for reports and history, and use a webhook to start the next step the moment a view happens.

Table comparing API polling and a webhook on five questions about one proposal opened at 9:32 Polling the API vs receiving a webhook QUESTION API POLLING (PULL) WEBHOOK (PUSH) Who makes the call? Your script asks using your API key HTMLvault calls you an HTTP POST to your URL Prospect opens at 9:32 You find out at 9:45 on the next 15-minute poll You find out at 9:32 as the view happens Requests in 8 hours 960, almost all empty 30 links, every 15 minutes One per view no empty requests If your server is down Next poll catches up views stay in HTMLvault Event may arrive twice handler must skip repeats Last week's opens Yes, any period your account still keeps No, it has no memory only events after setup USE IT FOR Reports and backfill Starting the next step
In the webhook vs API choice, neither replaces the other: the API is the record you can query at any time, and the webhook is the signal that something just changed.

Who calls whom. With polling, your code sends a request authenticated with an API key and HTMLvault answers. With a webhook, HTMLvault makes the call and your endpoint answers. Every other row follows from that switch.

Speed and wasted calls. A poller only finds out about a view at its next check, so the worst-case delay equals the interval. Checking 30 proposal links every 15 minutes through an eight-hour day takes 960 requests and can still be 15 minutes late. Cut the interval to one minute and the delay shrinks, but the count rises to 14,400 requests, almost all of which return nothing new. A webhook has no interval to tune.

Failure. If a poller crashes, the next run asks again and catches up, because the data is still in HTMLvault. If a webhook receiver is down, the delivery has to be attempted again, so the same event can reach you twice. Webhooks: Real-Time Link Events for Your Automation Stack covers how deliveries and retries work. For this post, one consequence is enough: your handler must cope with repeats.

History. A webhook only reports what happens after you register it. The API can answer for any period your account still keeps, so backfill and reporting stay on the pull side.

How the recipient.viewed webhook works

What triggers it. A recipient link is a tracked link made for one named person, so each contact on a proposal gets their own URL and their own analytics. An AI tool connected through the MCP server (Claude, ChatGPT, Gemini or Cursor) creates them with the create_recipient_links tool. When someone opens one, HTMLvault fires recipient.viewed and sends it to your webhook URL.

Use it instead of link.viewed when the link belongs to a person. A link view tells you a page was opened. A recipient view tells you whose link it was, and a follow-up task needs that to reach the right contact and the right owner.

What the payload carries. Each delivery is a JSON body that names the event and identifies the recipient link that was viewed. Treat it as a trigger, not a report. The engagement detail lives in the link's analytics: total and unique views, repeat visits, scroll depth, time on page, country and city, device and browser, referrer and channel. You read it with get_analytics over MCP or through the REST API. Map fields from a real delivery, never from a guess. The receiver code below is written that way on purpose.

Plan. Webhooks start on Pro ($12 a month, or $10 a month billed annually), which allows up to five.

Wire recipient.viewed into a CRM task with Zapier or Clay

Most teams never write a receiver. They point the webhook at a tool that already has one. The goal in this webhook example: when a contact opens their proposal, a task appears in the CRM, assigned to the account owner and due today.

A recipient.viewed webhook example end to end: the open becomes an assigned CRM task without anyone watching a dashboard.

Zapier

  1. Create a Zap with the Webhooks by Zapier trigger and choose Catch Hook. Copy the URL Zapier generates.
  2. Register that URL as an HTMLvault webhook for recipient.viewed.
  3. Create a recipient link addressed to yourself and open it in a browser. A real delivery reaches Zapier, and the trigger step then lists the actual fields to map.
  4. Add a Filter step that continues only when the event name is recipient.viewed. That keeps the Zap correct if other events are sent to the same URL later.
  5. Add a CRM search step that looks for an open follow-up task for this contact, and continue only if none exists. This step makes the Zap safe to run twice.
  6. Add your CRM's create-task action. Put the recipient in the subject, set today as the due date, make the account owner the assignee, and add the link reference to the notes so the rep knows which proposal was opened.

Clay

Clay can receive the same delivery into a table through a webhook source, one row per event. From there it can enrich the row with company and owner data and send it on to your CRM. Use Clay when the follow-up needs context the event doesn't carry. Use Zapier when the task just needs to exist.

A minimal webhook receiver in code

If you'd rather run the receiver yourself, it's a single route. These rules apply in any language:

  1. Accept an HTTP POST with a JSON body at a public HTTPS URL.
  2. Acknowledge events you don't handle with a 2xx status and do nothing else.
  3. Build an idempotency key from the event (here, link plus recipient) and skip the work if that key was already handled. Idempotent means running it twice has the same effect as running it once.
  4. Do the work, record the key, then return 2xx. If the work fails, return 5xx and don't record the key.
  5. Take field paths from a real delivery. Never guess them.
// recipient.viewed receiver: Node 18+, Express
import express from "express";

const app = express();
const handled = new Set(); // use a database table in production

// Placeholders on purpose: copy each path from a real delivery.
const FIELD = {
  event: "PATH_TO_EVENT_NAME",
  link: "PATH_TO_LINK_ID",
  recipient: "PATH_TO_RECIPIENT",
};
const read = (body, path) =>
  path.split(".").reduce((value, key) => value?.[key], body);

app.post("/hooks/htmlvault", express.json(), async (req, res) => {
  const body = req.body;
  if (read(body, FIELD.event) !== "recipient.viewed") {
    return res.sendStatus(204); // not this handler's event
  }

  const key = `${read(body, FIELD.link)}|${read(body, FIELD.recipient)}`;
  if (handled.has(key)) {
    return res.sendStatus(200); // repeat delivery or repeat open
  }

  try {
    await createCrmTask({
      subject: `Follow up: ${read(body, FIELD.recipient)} opened the proposal`,
      due: new Date().toISOString().slice(0, 10),
    });
    handled.add(key);
    res.sendStatus(200);
  } catch (err) {
    res.sendStatus(500); // failed: key stays unrecorded
  }
});

app.listen(3000);

createCrmTask stands in for your CRM's API call. This key allows one task per recipient per proposal. If a return visit three days later should create a new task, add the date to the key. Replace the in-memory set with a database table before running this anywhere real, because a restart empties it.

Limits, and when to use the API instead

  • The endpoint must be reachable from the internet. HTMLvault can't post to localhost or to a server behind your VPN. Use the hosted URL Zapier or Clay gives you, deploy the receiver, or test through a tunnel that exposes a local port.
  • Handlers must be safe to run twice. A delivery can repeat, and a contact can open the link again. Either way, the second run should find its key and stop.
  • A webhook has no memory. Opens from before you registered it never arrive, and a long outage on your side can leave gaps. Pull those once through the API and reconcile them against what your CRM already holds.
  • Reporting is a pull job. The weekly view of which proposals were read, how far and for how long comes from per-link analytics, not from a pile of events.
  • Pro allows five webhooks. Send several events to one receiver or Zap and route them inside it, instead of using up an endpoint per workflow.

The API covers history and reporting; recipient.viewed covers the moment itself. When a contact opens their proposal, the follow-up task is already in the CRM with an owner and a due date before the rep has opened a single dashboard.

webhooksrest apirecipient linkszapiercrm automationrevopsProduct Feature
HTMLvault

Publish your AI creations. See who reads them.

Proposals, dashboards and reports from Claude, ChatGPT or any AI tool become private links on your own domain. Update them after you send, and see who read what, down to the scroll.

Create a link

Related posts