Every monday.com integration I have built in the last two years has started with the same question. Do I poll the API on a schedule, or do I wire up a webhook? The honest answer is that both work, but the one you pick on day one shapes how painful the next two years of maintenance will be.
This post is the decision framework I wish someone had handed me when I was first wiring monday.com into Make.com, Zapier, and custom Node services. It covers when each approach actually wins, the specific failure modes of each, and code for the two patterns that solve 80% of real integrations.
The short version
Pick webhooks when you need near-real-time reaction to a specific event, the integration is one-way (monday to somewhere), and the target system can accept an inbound HTTP request. Pick polling when you need to aggregate across many items, the downstream system cannot receive webhooks cleanly, or you need retry logic and ordering guarantees that webhooks cannot give you.
Most teams default to whichever was easier to set up that day. That is why you end up with fragile integrations.
What webhooks actually give you
A webhook on monday.com is a subscription to a specific event on a specific board. Monday calls your endpoint with a JSON payload when something matching the event fires. The events you can subscribe to include:
create_itemchange_column_valuechange_status_column_valuechange_specific_column_valueitem_deleteditem_archiveditem_moved_to_any_groupsubitem_createdupdate_created
The minimum viable webhook is three lines of monday GraphQL:
mutation {
create_webhook(
board_id: 1234567890
url: "https://yourserver.com/monday-webhook"
event: change_status_column_value
config: "{\"columnId\":\"status\",\"columnValue\":{\"$any$\":true}}"
) { id }
}
Your endpoint needs to handle two things on the first request: a challenge response, and then actual events after that.
// Express handler
app.post('/monday-webhook', (req, res) => {
// Monday sends a challenge on webhook creation
if (req.body.challenge) {
return res.json({ challenge: req.body.challenge });
}
// Real event
const { event } = req.body;
const itemId = event.pulseId;
const newStatus = event.value?.label?.text;
// Do your thing
processStatusChange(itemId, newStatus);
res.sendStatus(200);
});
That is webhooks in their best light. Fast, low-latency, simple.
Where webhooks fall apart
Lost events. Monday retries a failed webhook delivery, but only a handful of times over a short window. If your server is down for an hour, events fired during that window are gone. There is no replay endpoint.
No ordering guarantee. If two column changes happen 50ms apart, they can arrive at your server out of order. For status flows where order matters, this bites.
Per-board setup. Webhooks are scoped to one board. If you have twelve boards that all need the same automation, you need twelve webhook subscriptions. A new board created tomorrow does not automatically inherit them.
Public endpoint required. Your integration needs a stable, publicly accessible URL. This rules out a lot of internal tools and makes local development annoying.
Payload is thin. The webhook body does not include the full item state. It tells you what changed, but if your handler needs the full board context, you are making an additional GraphQL call anyway.
What polling actually gives you
Polling is the opposite end of the tradeoff. You query the monday API on a schedule, diff the result against your last known state, and act on the differences. It is slower, heavier on API quota, but gives you things webhooks cannot.
The pattern that works in production:
async function pollBoard(boardId, lastCheckedAt) {
const query = `
query ($boardId: ID!, $cursor: String) {
boards(ids: [$boardId]) {
items_page(limit: 50, cursor: $cursor) {
cursor
items {
id
name
updated_at
column_values { id text value }
}
}
}
}
`;
let allItems = [];
let cursor = null;
do {
const res = await mondayClient.query(query, { boardId, cursor });
const page = res.data.boards[0].items_page;
allItems = allItems.concat(page.items);
cursor = page.cursor;
} while (cursor);
// Only act on items changed since last check
return allItems.filter(item => item.updated_at > lastCheckedAt);
}
The key detail is updated_at. Filtering on the server side cuts quota significantly, and cursoring lets you handle boards with thousands of items without blowing the 5MB response cap.
Why polling wins more often than devs think
You cannot miss events. The full board state is authoritative on every poll. If you were down for an hour, the next poll catches up.
You can diff against custom state. Webhooks tell you what monday thinks changed. Polling lets you compare against your own historical view, which matters when you are computing derived values like velocity, drift, or SLA breaches.
Ordering is implicit. You process items in whatever order you choose. Sort by
updated_at, by priority, by whatever makes sense for your downstream system.No public endpoint needed. Polling runs from your infrastructure outward. It works from inside a VPN, from a cron job on a laptop, from a GitHub Action, from anywhere.
One job covers N boards. Your poll loop can take a list of board IDs from config. Adding a new board is a config change, not a deploy.
Where polling falls apart
Latency. You only know about changes at the next poll tick. A 5-minute interval means up to 5 minutes of delay. Some workflows do not tolerate that.
API quota. Monday enforces complexity limits. Polling 20 boards every minute with full column values will hit the cap faster than you expect.
You write the diff logic. It is not complex, but it is your responsibility. Missed state, drift between your store and monday, all on you.
The hybrid pattern that solves most real cases
This is the pattern I use on every non-trivial integration now.
Webhooks fire on events that need immediate action. A polling job runs every 15 minutes and reconciles the authoritative state. The webhook is the low-latency path for urgent events. The poll is the safety net that catches anything the webhook missed.
// Webhook handler: immediate path
app.post('/monday-webhook', async (req, res) => {
if (req.body.challenge) return res.json({ challenge: req.body.challenge });
await queue.enqueue({
source: 'webhook',
itemId: req.body.event.pulseId,
event: req.body.event.type
});
res.sendStatus(200);
});
// Polling job: reconciliation path
async function reconcileBoards(boardIds, since) {
for (const boardId of boardIds) {
const changed = await pollBoard(boardId, since);
for (const item of changed) {
await queue.enqueue({
source: 'poll',
itemId: item.id,
snapshot: item
});
}
}
}
// Single worker dedupes by itemId, processes in order
worker.process(async (job) => {
const { itemId } = job.data;
const current = await fetchItem(itemId);
await handleItem(current);
});
The queue deduplicates events from both sources on item ID. Your downstream logic runs once per real change, regardless of whether the webhook or the poll surfaced it.
This pattern survives contact with production. I have had webhook endpoints go down for half a day and lose zero events because the poll caught up. I have had polls hit quota limits and not felt latency because webhooks kept the critical path hot.
Picking the right default
If you are wiring monday.com to Make.com, Zapier, or any low-code tool: start with webhooks. The latency matters for user-facing flows, and the tools handle retries reasonably well.
If you are building a custom service, or integrating with a data warehouse, or computing anything derived from board state: start with polling. You will want the safety net, and the latency rarely matters for analytical workloads.
If you are doing anything mission-critical, or where lost events cost real money: combine both. The hybrid pattern is the only approach that does not wake you up at 3am because a webhook delivery failed during an AWS region hiccup.
The full implementation patterns for all three approaches, including retry logic, cursor management, and rate limit handling, are written up in the monday.com automation recipes guide at migratetomonday.com. Take what is useful, skip what is not.
Happy to answer questions in the comments if you are wrestling with a specific integration. Most of the real learnings come from the edge cases.
Top comments (0)