<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Gwilym Pugh</title>
    <description>The latest articles on Forem by Gwilym Pugh (@gwilym_ge).</description>
    <link>https://forem.com/gwilym_ge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3862257%2Fe9dd681c-ea95-4d97-a0a8-83f63e913afc.png</url>
      <title>Forem: Gwilym Pugh</title>
      <link>https://forem.com/gwilym_ge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gwilym_ge"/>
    <language>en</language>
    <item>
      <title>Webhooks vs. Polling on monday.com: When Each Approach Actually Wins</title>
      <dc:creator>Gwilym Pugh</dc:creator>
      <pubDate>Wed, 15 Apr 2026 21:08:54 +0000</pubDate>
      <link>https://forem.com/gwilym_ge/webhooks-vs-polling-on-mondaycom-when-each-approach-actually-wins-2j37</link>
      <guid>https://forem.com/gwilym_ge/webhooks-vs-polling-on-mondaycom-when-each-approach-actually-wins-2j37</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Most teams default to whichever was easier to set up that day. That is why you end up with fragile integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What webhooks actually give you
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create_item&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;change_column_value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;change_status_column_value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;change_specific_column_value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;item_deleted&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;item_archived&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;item_moved_to_any_group&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subitem_created&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update_created&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The minimum viable webhook is three lines of monday GraphQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;create_webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;board_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1234567890&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://yourserver.com/monday-webhook"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change_status_column_value&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{\"columnId\":\"status\",\"columnValue\":{\"$any$\":true}}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your endpoint needs to handle two things on the first request: a challenge response, and then actual events after that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express handler&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/monday-webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Monday sends a challenge on webhook creation&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Real event&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pulseId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Do your thing&lt;/span&gt;
  &lt;span class="nf"&gt;processStatusChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is webhooks in their best light. Fast, low-latency, simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where webhooks fall apart
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lost events.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No ordering guarantee.&lt;/strong&gt; If two column changes happen 50ms apart, they can arrive at your server out of order. For status flows where order matters, this bites.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Per-board setup.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public endpoint required.&lt;/strong&gt; Your integration needs a stable, publicly accessible URL. This rules out a lot of internal tools and makes local development annoying.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Payload is thin.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What polling actually gives you
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The pattern that works in production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pollBoard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;boardId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastCheckedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    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 }
          }
        }
      }
    }
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mondayClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;boardId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;items_page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;allItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Only act on items changed since last check&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;allItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;lastCheckedAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key detail is &lt;code&gt;updated_at&lt;/code&gt;. Filtering on the server side cuts quota significantly, and cursoring lets you handle boards with thousands of items without blowing the 5MB response cap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why polling wins more often than devs think
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You cannot miss events.&lt;/strong&gt; The full board state is authoritative on every poll. If you were down for an hour, the next poll catches up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You can diff against custom state.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ordering is implicit.&lt;/strong&gt; You process items in whatever order you choose. Sort by &lt;code&gt;updated_at&lt;/code&gt;, by priority, by whatever makes sense for your downstream system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No public endpoint needed.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One job covers N boards.&lt;/strong&gt; Your poll loop can take a list of board IDs from config. Adding a new board is a config change, not a deploy.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where polling falls apart
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Latency.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API quota.&lt;/strong&gt; Monday enforces complexity limits. Polling 20 boards every minute with full column values will hit the cap faster than you expect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You write the diff logic.&lt;/strong&gt; It is not complex, but it is your responsibility. Missed state, drift between your store and monday, all on you.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The hybrid pattern that solves most real cases
&lt;/h2&gt;

&lt;p&gt;This is the pattern I use on every non-trivial integration now.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Webhook handler: immediate path&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/monday-webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pulseId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Polling job: reconciliation path&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reconcileBoards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;boardIds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;since&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;boardId&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;boardIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pollBoard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;boardId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;since&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;poll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Single worker dedupes by itemId, processes in order&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking the right default
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The full implementation patterns for all three approaches, including retry logic, cursor management, and rate limit handling, are written up in the &lt;a href="https://www.migratetomonday.com/resources/guides/monday-crm-automation-recipes/" rel="noopener noreferrer"&gt;monday.com automation recipes guide&lt;/a&gt; at migratetomonday.com. Take what is useful, skip what is not.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Automation Patterns That Survive Real Teams</title>
      <dc:creator>Gwilym Pugh</dc:creator>
      <pubDate>Thu, 09 Apr 2026 08:13:59 +0000</pubDate>
      <link>https://forem.com/gwilym_ge/automation-patterns-that-survive-real-teams-3ef8</link>
      <guid>https://forem.com/gwilym_ge/automation-patterns-that-survive-real-teams-3ef8</guid>
      <description>&lt;p&gt;About 40% of the automations I build during a monday.com implementation have stopped working correctly within 90 days of go-live.&lt;/p&gt;

&lt;p&gt;Not because the automation engine failed. Because the business changed and nobody updated the automation. The status label got renamed. The person who owned the workflow moved teams. A new column was added and the old one became redundant. The automation kept firing, just on the wrong conditions, producing quietly broken output that nobody noticed until a report looked wrong two months later.&lt;/p&gt;

&lt;p&gt;This isn't a monday.com problem. It's a pattern I've seen across every platform I've worked with. The automations that survive aren't the clever ones. They're the ones built with structural awareness that the business will change faster than the automation.&lt;/p&gt;

&lt;p&gt;Here are the patterns that actually last, drawn from 50+ SMB implementations across construction, recruitment, insurance, and financial services.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Target the 80% case, not the edge cases
&lt;/h2&gt;

&lt;p&gt;The biggest killer of automation reliability is trying to handle every possible scenario.&lt;/p&gt;

&lt;p&gt;You build an automation to route new leads to the right sales rep based on territory. The happy path is simple: US leads go to the US team, EU leads go to the EU team. Then someone asks "what about Mexico?" So you add a rule. Then "what about contractors based in Spain but selling to Latin America?" Another rule. Then "what if the contact is from the US but the company is based in Germany?" Three more rules.&lt;/p&gt;

&lt;p&gt;After six weeks your lead routing automation has 14 conditional branches, three of which contradict each other, and nobody can remember why rule #9 exists.&lt;/p&gt;

&lt;p&gt;The better pattern: handle the 80% case cleanly, route the rest to a human. In monday.com terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="nf"&gt;When &lt;/span&gt;new lead created
  &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;country&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;US&lt;/span&gt;
  &lt;span class="nf"&gt;Then &lt;/span&gt;assign to US Sales group

&lt;span class="nf"&gt;When &lt;/span&gt;new lead created
  &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;country&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;UK&lt;/span&gt;
  &lt;span class="err"&gt;OR&lt;/span&gt; &lt;span class="err"&gt;country&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;Germany&lt;/span&gt;
  &lt;span class="err"&gt;OR&lt;/span&gt; &lt;span class="err"&gt;country&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;France&lt;/span&gt;
  &lt;span class="nf"&gt;Then &lt;/span&gt;assign to EU Sales group

&lt;span class="nf"&gt;When &lt;/span&gt;new lead created
  &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt; &lt;span class="err"&gt;previous&lt;/span&gt; &lt;span class="err"&gt;rule&lt;/span&gt; &lt;span class="err"&gt;matched&lt;/span&gt;
  &lt;span class="nf"&gt;Then &lt;/span&gt;assign to &lt;span class="s"&gt;"Needs Review"&lt;/span&gt; group
  &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;notify&lt;/span&gt; &lt;span class="err"&gt;Sales&lt;/span&gt; &lt;span class="err"&gt;Ops&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Needs Review" bucket is where edge cases go to be handled by a human. A week in production tells you which edge cases are actually common enough to automate. The ones that happen twice in three months stay manual forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Build a manual override path for every automated decision
&lt;/h2&gt;

&lt;p&gt;Every automation should have a documented way to override it.&lt;/p&gt;

&lt;p&gt;If an automation assigns a deal owner based on territory, there should be a simple way for a sales manager to reassign it without breaking the automation's future behaviour. If an automation moves a project to "In Progress" when the client approves the proposal, there should be a way to move it back to "Pending" if the client changes their mind without the automation immediately fighting back.&lt;/p&gt;

&lt;p&gt;The anti-pattern: an automation that instantly undoes any manual change because it sees the "wrong" state.&lt;/p&gt;

&lt;p&gt;The fix in monday.com: use a status column for the automation's decision and a separate checkbox column (like "Locked by user") that the automation checks first. If locked, the automation skips. If unlocked, it runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="nf"&gt;When &lt;/span&gt;Status column changes
  &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;Locked&lt;/span&gt; &lt;span class="err"&gt;by&lt;/span&gt; &lt;span class="err"&gt;user&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;false&lt;/span&gt;
  &lt;span class="nf"&gt;Then &lt;/span&gt;[automation action]

&lt;span class="nf"&gt;When &lt;/span&gt;Status column changes
  &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;Locked&lt;/span&gt; &lt;span class="err"&gt;by&lt;/span&gt; &lt;span class="err"&gt;user&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;true&lt;/span&gt;
  &lt;span class="nf"&gt;Then &lt;/span&gt;do nothing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small structural change is the difference between an automation the team trusts and one they work around by building a parallel spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Document trigger, action, and owner in one sentence each
&lt;/h2&gt;

&lt;p&gt;Every automation needs three pieces of documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger:&lt;/strong&gt; What fires this automation? (One sentence.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; What does it do? (One sentence.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner:&lt;/strong&gt; Who maintains this? (A name, not a team.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can't write each of these in one sentence, the automation is too complex and will break.&lt;/p&gt;

&lt;p&gt;Keep the documentation somewhere the team can actually find it. A dedicated "Automation Registry" board in your workspace is better than a buried Confluence page. Each row is an automation. Columns are: Name, Board, Trigger, Action, Owner, Last Reviewed, Status.&lt;/p&gt;

&lt;p&gt;Here's the schema I use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Board&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Automation Registry&lt;/span&gt;
&lt;span class="na"&gt;Columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Name (text)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Parent Board (board relation)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Trigger Description (long text, 1 sentence)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Action Description (long text, 1 sentence)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Owner (person)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Last Reviewed (date)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Active? (checkbox)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Notes (long text, optional)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When something changes (new column added, new team member joined, business process updated), the team can open the registry and see which automations need reviewing. Without this, you rediscover broken automations through downstream bugs months later.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Weekly five-minute health check
&lt;/h2&gt;

&lt;p&gt;Automations need maintenance. The teams whose automations still work a year later are the teams that review them weekly.&lt;/p&gt;

&lt;p&gt;The review doesn't need to be complicated. Five minutes, once a week:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Automation Registry board&lt;/li&gt;
&lt;li&gt;Check which automations fired this week (monday.com shows activity logs per automation)&lt;/li&gt;
&lt;li&gt;Check which ones failed or skipped&lt;/li&gt;
&lt;li&gt;Scan the outputs they produced (did the notifications actually land? Did the status changes actually stick? Are the reports still correct?)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This single habit catches silent breakages before they become noisy problems. A renamed status label, a removed column, a changed field type. All of these can silently disable an automation. Weekly checks surface them before your quarterly board report shows wrong numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Train the maintainer, not just the users
&lt;/h2&gt;

&lt;p&gt;Most automation training focuses on the end users. The sales reps who benefit from lead routing. The project managers whose status updates get mirrored automatically. The ops team whose weekly reports generate themselves.&lt;/p&gt;

&lt;p&gt;This matters, but it's not the critical training.&lt;/p&gt;

&lt;p&gt;The critical training is for the person or people who maintain the automations after go-live. They need to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How each automation works, structurally&lt;/li&gt;
&lt;li&gt;How to diagnose when one isn't firing&lt;/li&gt;
&lt;li&gt;How to modify it safely when the business process changes&lt;/li&gt;
&lt;li&gt;When to remove an automation rather than patch it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this, any business change either breaks the system or forces the team to work around it. The person who set up the automations six months ago has long since forgotten the subtle reasons for certain design decisions, and the consultant has moved on. The result is a slow erosion of trust in the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example: CRM pipeline automation
&lt;/h2&gt;

&lt;p&gt;Here's a full pattern I've implemented dozens of times. This handles the end-to-end deal progression in monday.com CRM without ever hitting the "too clever" failure mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boards involved:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deals (primary)&lt;/li&gt;
&lt;li&gt;Accounts&lt;/li&gt;
&lt;li&gt;Projects (delivery board)&lt;/li&gt;
&lt;li&gt;Team Capacity (resource planning)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Automations:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="err"&gt;1.&lt;/span&gt; &lt;span class="nf"&gt;When &lt;/span&gt;Deal Status changes to &lt;span class="s"&gt;"Won"&lt;/span&gt;
   &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;Connected&lt;/span&gt; &lt;span class="err"&gt;Account&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;not&lt;/span&gt; &lt;span class="err"&gt;empty&lt;/span&gt;
   &lt;span class="err"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Move&lt;/span&gt; &lt;span class="err"&gt;deal&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;"Closed&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Won"&lt;/span&gt; &lt;span class="err"&gt;group&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Set&lt;/span&gt; &lt;span class="err"&gt;Close&lt;/span&gt; &lt;span class="nf"&gt;Date &lt;/span&gt;to today
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Create&lt;/span&gt; &lt;span class="err"&gt;item&lt;/span&gt; &lt;span class="err"&gt;on&lt;/span&gt; &lt;span class="err"&gt;Projects&lt;/span&gt; &lt;span class="err"&gt;board&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Connect&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;new&lt;/span&gt; &lt;span class="err"&gt;project&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;deal&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Notify&lt;/span&gt; &lt;span class="err"&gt;Delivery&lt;/span&gt; &lt;span class="err"&gt;Lead&lt;/span&gt; &lt;span class="err"&gt;with&lt;/span&gt; &lt;span class="err"&gt;deal&lt;/span&gt; &lt;span class="err"&gt;context&lt;/span&gt;

&lt;span class="err"&gt;2.&lt;/span&gt; &lt;span class="nf"&gt;When &lt;/span&gt;Project created from Won deal
   &lt;span class="err"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Mirror&lt;/span&gt; &lt;span class="err"&gt;Deal&lt;/span&gt; &lt;span class="err"&gt;Value&lt;/span&gt; &lt;span class="err"&gt;from&lt;/span&gt; &lt;span class="err"&gt;Deals&lt;/span&gt; &lt;span class="err"&gt;board&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Mirror&lt;/span&gt; &lt;span class="err"&gt;Client&lt;/span&gt; &lt;span class="err"&gt;Contact&lt;/span&gt; &lt;span class="err"&gt;from&lt;/span&gt; &lt;span class="err"&gt;Accounts&lt;/span&gt; &lt;span class="err"&gt;board&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Set&lt;/span&gt; &lt;span class="err"&gt;Project&lt;/span&gt; &lt;span class="err"&gt;Status&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;"Kickoff&lt;/span&gt; &lt;span class="err"&gt;Pending"&lt;/span&gt;
     &lt;span class="err"&gt;- Create three default subitems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;Kickoff,&lt;/span&gt; &lt;span class="err"&gt;Scope,&lt;/span&gt; &lt;span class="err"&gt;Delivery&lt;/span&gt; &lt;span class="err"&gt;Plan&lt;/span&gt;

&lt;span class="err"&gt;3.&lt;/span&gt; &lt;span class="nf"&gt;When &lt;/span&gt;Project Status changes to &lt;span class="s"&gt;"Kickoff Complete"&lt;/span&gt;
   &lt;span class="err"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Update&lt;/span&gt; &lt;span class="err"&gt;connected&lt;/span&gt; &lt;span class="err"&gt;Deal&lt;/span&gt; &lt;span class="err"&gt;status&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;"Delivery&lt;/span&gt; &lt;span class="err"&gt;Active"&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Notify&lt;/span&gt; &lt;span class="err"&gt;Account&lt;/span&gt; &lt;span class="err"&gt;Manager&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Update&lt;/span&gt; &lt;span class="err"&gt;Team&lt;/span&gt; &lt;span class="err"&gt;Capacity&lt;/span&gt; &lt;span class="err"&gt;board&lt;/span&gt; &lt;span class="err"&gt;with&lt;/span&gt; &lt;span class="err"&gt;assigned&lt;/span&gt; &lt;span class="err"&gt;resources&lt;/span&gt;

&lt;span class="err"&gt;4.&lt;/span&gt; &lt;span class="nf"&gt;When &lt;/span&gt;Deal has been in &lt;span class="s"&gt;"Proposal Sent"&lt;/span&gt; &amp;gt; 14 days
   &lt;span class="err"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;Deal&lt;/span&gt; &lt;span class="err"&gt;Status&lt;/span&gt; &lt;span class="err"&gt;has&lt;/span&gt; &lt;span class="err"&gt;not&lt;/span&gt; &lt;span class="err"&gt;changed&lt;/span&gt;
   &lt;span class="err"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Flag&lt;/span&gt; &lt;span class="err"&gt;deal&lt;/span&gt; &lt;span class="err"&gt;as&lt;/span&gt; &lt;span class="err"&gt;"Stalled"&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Notify&lt;/span&gt; &lt;span class="err"&gt;Deal&lt;/span&gt; &lt;span class="err"&gt;Owner&lt;/span&gt; &lt;span class="err"&gt;with&lt;/span&gt; &lt;span class="err"&gt;nudge&lt;/span&gt;
     &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;Do &lt;/span&gt;NOT auto-send follow-up email (human decides)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what this pattern does and doesn't do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does:&lt;/strong&gt; Handle the predictable state transitions cleanly. Push context across boards. Alert humans when judgement is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Doesn't:&lt;/strong&gt; Auto-send outreach emails. Auto-assign deals to specific reps. Auto-adjust pricing or terms. These are human decisions that automation should prompt, not make.&lt;/p&gt;

&lt;p&gt;The automations that survive are the ones that reduce admin, not the ones that try to replace judgement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The principle behind all of this
&lt;/h2&gt;

&lt;p&gt;Every pattern above is a specific expression of one underlying principle: &lt;strong&gt;build automations for how the business actually works, not how it theoretically should work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Theoretical processes don't have exceptions. Real ones do. Theoretical users don't forget to update fields. Real ones do. Theoretical data is clean. Real data has duplicates, typos, and fields nobody has touched in 18 months.&lt;/p&gt;

&lt;p&gt;The automations that work in production are the ones designed by someone who's seen how the business really operates, built to handle the predictable cases cleanly and route everything else to a human. The clever edge-case handling goes in version 2, after a month in production reveals which edge cases are actually common.&lt;/p&gt;

&lt;p&gt;If you want more concrete patterns, I've collected the ones I use most often as a set of &lt;a href="https://www.migratetomonday.com/resources/guides/monday-crm-automation-recipes/" rel="noopener noreferrer"&gt;monday.com automation recipes&lt;/a&gt; covering lead intake, assignment routing, follow-ups, deal progression, and reporting. They're all built on the patterns above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related reading
&lt;/h2&gt;

&lt;p&gt;Previous entry in this series: &lt;a href="https://dev.to/gwilym_ge/how-to-structure-a-mondaycom-workspace-for-multi-department-operations-5d98"&gt;How to Structure a monday.com Workspace for Multi-Department Operations&lt;/a&gt;. The workspace architecture you choose determines which automation patterns are even possible, so it's worth reading that piece first if you're setting up a new instance.&lt;/p&gt;

</description>
      <category>projectmanagement</category>
      <category>saas</category>
      <category>productivity</category>
      <category>nocode</category>
    </item>
    <item>
      <title>How to Structure a monday.com Workspace for Multi-Department Operations</title>
      <dc:creator>Gwilym Pugh</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:43:00 +0000</pubDate>
      <link>https://forem.com/gwilym_ge/how-to-structure-a-mondaycom-workspace-for-multi-department-operations-5d98</link>
      <guid>https://forem.com/gwilym_ge/how-to-structure-a-mondaycom-workspace-for-multi-department-operations-5d98</guid>
      <description>&lt;p&gt;When your company hits 30-50 people across three or more departments, your monday.com instance stops being a collection of boards and starts being an architectural decision. How you structure workspaces, boards, and permissions at this stage determines whether the platform scales with you or becomes another tool people work around.&lt;/p&gt;

&lt;p&gt;I've set up monday.com for companies across construction, recruitment, financial services, and healthcare. The pattern is consistent: teams that think about workspace architecture early save themselves a painful restructuring 6-12 months later. Teams that don't end up with 200 boards scattered across one workspace, no naming conventions, and permissions that either lock everyone out or let everyone see everything.&lt;/p&gt;

&lt;p&gt;Here's what I've learned about structuring workspaces for multi-department operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Single-Workspace Trap
&lt;/h2&gt;

&lt;p&gt;Most companies start with one workspace. Marketing has their boards, Sales has theirs, Operations has theirs. It works until it doesn't.&lt;/p&gt;

&lt;p&gt;The breaking point usually comes from one of three places:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission sprawl.&lt;/strong&gt; Your HR team has a board tracking employee reviews. It's in the same workspace as the marketing content calendar. Everyone can see both. You can set board-level permissions, but with 40 boards in one workspace, managing individual board permissions becomes a full-time job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Information overload.&lt;/strong&gt; When someone opens monday.com, they see every board in the workspace. A salesperson doesn't need to see the engineering sprint board. A recruiter doesn't need to see the finance reporting dashboard. But they're all there, cluttering the sidebar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Naming collision.&lt;/strong&gt; Marketing has a "Tasks" board. Operations has a "Tasks" board. Engineering has a "Tasks" board. Search becomes useless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Multi-Workspace Model
&lt;/h2&gt;

&lt;p&gt;The fix is straightforward: one workspace per department or functional area. A typical structure for a 40-person company:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company Account
├── Sales &amp;amp; CRM (workspace)
│   ├── Deals Pipeline
│   ├── Contacts
│   ├── Accounts
│   └── Sales Reports Dashboard
├── Operations (workspace)
│   ├── Project Delivery
│   ├── Resource Planning
│   ├── Client Onboarding
│   └── Ops Dashboard
├── Marketing (workspace)
│   ├── Content Calendar
│   ├── Campaign Tracker
│   ├── SEO Pipeline
│   └── Marketing Dashboard
├── HR &amp;amp; Admin (workspace)
│   ├── Recruitment Pipeline
│   ├── Employee Directory
│   ├── Leave Tracker
│   └── Onboarding Checklist
└── Company-Wide (workspace)
    ├── All-Hands Meeting Notes
    ├── OKRs / Goals
    ├── IT Requests
    └── Cross-Department Projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each workspace has its own membership. Sales team members are added to the Sales workspace. They can see everything in Sales, nothing in HR. If someone needs cross-department visibility (like a COO), they're added to multiple workspaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cross-Department Problem
&lt;/h2&gt;

&lt;p&gt;Separate workspaces solve the permission and clutter problems. They create a new one: how do departments share data?&lt;/p&gt;

&lt;p&gt;A sales deal that closes needs to trigger an operations onboarding workflow. A marketing campaign needs to reference the sales pipeline to calculate ROI. A new hire in HR needs to appear on the IT equipment provisioning board.&lt;/p&gt;

&lt;p&gt;Monday.com solves this with &lt;strong&gt;connected boards&lt;/strong&gt; and &lt;strong&gt;mirror columns&lt;/strong&gt;. Here's how they work in practice:&lt;/p&gt;

&lt;h3&gt;
  
  
  Connected Boards
&lt;/h3&gt;

&lt;p&gt;A board relation column lets you link items on one board to items on another board, even across workspaces. Your Deals board in the Sales workspace can have a column that connects each deal to a Project on the Operations board.&lt;/p&gt;

&lt;p&gt;When a salesperson closes a deal, they link it to the corresponding project. Operations can now see which deal originated the project. Sales can see the project delivery status without leaving their workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mirror Columns
&lt;/h3&gt;

&lt;p&gt;Once boards are connected, mirror columns let you pull specific data from the connected board into your current view. Operations doesn't need to open the Sales workspace to see the deal value or client contact. They create a mirror column that reflects that data from the Deals board.&lt;/p&gt;

&lt;p&gt;The data stays in one place (the Deals board owns the deal value). The mirror just displays it. If Sales updates the deal value, the mirror updates automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Practical Pattern
&lt;/h3&gt;

&lt;p&gt;For a typical sales-to-operations handoff:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sales workspace&lt;/strong&gt; has a Deals board with a "Connected Project" column (board relation to Operations &amp;gt; Project Delivery)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations workspace&lt;/strong&gt; has Project Delivery board with mirror columns pulling Deal Value, Client Contact, and Close Date from the connected deal&lt;/li&gt;
&lt;li&gt;An automation fires when Deal Status changes to "Won": create an item on Project Delivery, link it to the deal, notify the operations lead&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same pattern works for any cross-department workflow: marketing-to-sales lead handoff, HR-to-IT equipment provisioning, client onboarding across sales and operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming Conventions That Scale
&lt;/h2&gt;

&lt;p&gt;This sounds mundane until you have 80 boards and can't find anything. Establish conventions early:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boards:&lt;/strong&gt; &lt;code&gt;[Department] - [Purpose]&lt;/code&gt; or &lt;code&gt;[Department] - [Entity Type]&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sales - Deals Pipeline&lt;/li&gt;
&lt;li&gt;Ops - Project Delivery
&lt;/li&gt;
&lt;li&gt;HR - Recruitment Pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Groups within boards:&lt;/strong&gt; Use consistent status-based names&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New / Incoming&lt;/li&gt;
&lt;li&gt;In Progress / Active&lt;/li&gt;
&lt;li&gt;Review / Approval&lt;/li&gt;
&lt;li&gt;Done / Archived&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Views:&lt;/strong&gt; Name views by audience or purpose&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"My Tasks" (filtered to current user)&lt;/li&gt;
&lt;li&gt;"This Week" (filtered by date)&lt;/li&gt;
&lt;li&gt;"Client: [Name]" (filtered by client)&lt;/li&gt;
&lt;li&gt;"Manager Overview" (summary/dashboard view)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Permission Architecture
&lt;/h2&gt;

&lt;p&gt;Monday.com has three permission levels that matter for workspace design:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workspace membership:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Members&lt;/strong&gt; can see and edit all boards in the workspace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Viewers&lt;/strong&gt; can see but not edit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Board-level permissions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editable by everyone in the workspace&lt;/li&gt;
&lt;li&gt;Editable by board owner only (others can view)&lt;/li&gt;
&lt;li&gt;Private (only invited members)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Column-level permissions (Enterprise plan):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restrict who can edit specific columns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical setup for most companies:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Workspaces&lt;/th&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Department staff&lt;/td&gt;
&lt;td&gt;Own department + Company-Wide&lt;/td&gt;
&lt;td&gt;Member&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Department manager&lt;/td&gt;
&lt;td&gt;Own department + any they oversee&lt;/td&gt;
&lt;td&gt;Member&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C-suite / leadership&lt;/td&gt;
&lt;td&gt;All workspaces&lt;/td&gt;
&lt;td&gt;Member or Viewer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External contractors&lt;/td&gt;
&lt;td&gt;Specific boards only (via guest access)&lt;/td&gt;
&lt;td&gt;Guest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key principle:&lt;/strong&gt; Default to workspace-level membership for simplicity. Only use board-level restrictions for genuinely sensitive boards (compensation data, performance reviews, legal matters).&lt;/p&gt;

&lt;h2&gt;
  
  
  Automations Across Workspaces
&lt;/h2&gt;

&lt;p&gt;Cross-workspace automations are where the real operational value lives. The three most valuable patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Status-triggered item creation
&lt;/h3&gt;

&lt;p&gt;"When a deal moves to Won in Sales, create a project in Operations."&lt;/p&gt;

&lt;p&gt;This eliminates the manual handoff where someone in Sales emails Operations to say "we closed the deal, can you set up the project?" The project appears automatically with all relevant deal data mirrored.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Date-based notifications
&lt;/h3&gt;

&lt;p&gt;"When a project delivery date is 7 days away, notify the account manager in Sales."&lt;/p&gt;

&lt;p&gt;This keeps Sales informed about delivery timelines without requiring Operations to send manual updates. The account manager can proactively reach out to the client before delivery, not after.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Status-based dashboard updates
&lt;/h3&gt;

&lt;p&gt;"When a project status changes to Complete, update the client health score on the Accounts board."&lt;/p&gt;

&lt;p&gt;This creates a feedback loop between delivery and relationship management. If every project is delivered on time, the account health reflects that. If projects are consistently late, Sales sees the impact on client relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;After setting up workspaces for dozens of companies, these are the patterns I see teams get wrong:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Too many workspaces.&lt;/strong&gt; One team created a separate workspace for every client project. With 30 active clients, the workspace list became unmanageable. Rule of thumb: workspaces map to departments or functions, not to individual projects or clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Too few boards in a workspace.&lt;/strong&gt; The opposite problem: cramming everything into three mega-boards with 50 groups each. If a board has more than 10-12 groups, it probably needs to be split into separate boards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Company-Wide workspace.&lt;/strong&gt; Cross-departmental initiatives (OKRs, all-hands action items, IT requests) need a shared space. Without it, these items end up scattered across department workspaces or worse, in email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automations without ownership.&lt;/strong&gt; Someone builds 30 automations connecting Sales to Operations. That person leaves. Nobody knows which automations exist or what they do. Document every automation with a name, description, and owner. Keep a simple registry board: Automation Name, Trigger, Action, Owner, Last Updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you're restructuring an existing monday.com instance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit what exists.&lt;/strong&gt; List every board, who uses it, and which department it belongs to. You'll probably find boards nobody uses and boards that should be merged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design workspaces on paper first.&lt;/strong&gt; Map departments to workspaces. Identify cross-department data flows. Decide which boards need connected columns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Migrate one department at a time.&lt;/strong&gt; Move their boards to a new workspace, set up permissions, and let them run for a week before moving the next department.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build cross-workspace connections last.&lt;/strong&gt; Get each workspace stable before adding the complexity of connected boards and automations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a detailed walkthrough of workspace setup including permissions and governance, I've written a &lt;a href="https://www.migratetomonday.com/resources/guides/monday-workspace-tutorial/" rel="noopener noreferrer"&gt;workspace structure tutorial&lt;/a&gt; that covers the full process.&lt;/p&gt;

&lt;p&gt;The workspace architecture you choose today will either make monday.com feel like a unified operating system for your company, or like a slightly fancier collection of spreadsheets. The difference is 2-3 days of thoughtful planning upfront.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related reading
&lt;/h2&gt;

&lt;p&gt;If you're planning a CRM migration as part of restructuring how your team works, &lt;a href="https://seosandwitch.com/how-to-avoid-bad-crm-migration/" rel="noopener noreferrer"&gt;The Hidden Costs of a Bad CRM Migration&lt;/a&gt; covers the operational traps that catch most teams during the switch. The workspace architecture decisions described above are easier to make when you've already thought through what you're moving away from and why.&lt;/p&gt;

</description>
      <category>projectmanagement</category>
      <category>saas</category>
      <category>productivity</category>
      <category>nocode</category>
    </item>
  </channel>
</rss>
