> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modulex.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Co-edit a workflow canvas with your team

> Work on the same ModuleX workflow canvas at the same time as your teammates: live cursors, presence avatars, node locks, and automatic conflict handling — no saving, no overwriting.

export const MediaEmbed = ({id, type = 'screenshot', caption = '', ext, ratio = '16 / 9'}) => {
  const isVideo = type === 'video' || type === 'app_video';
  const resolvedExt = ext || (isVideo ? 'mp4' : type === 'screenshot' ? 'webp' : 'svg');
  const src = 'https://media.modulex.dev/' + id + '.' + resolvedExt;
  const [status, setStatus] = useState('loading');
  const [isDev, setIsDev] = useState(false);
  const [inView, setInView] = useState(false);
  const boxRef = useRef(null);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const h = window.location.hostname;
    setIsDev(h === 'localhost' || h === '127.0.0.1' || h.endsWith('.mintlify.app'));
  }, []);
  useEffect(() => {
    if (inView) return;
    if (typeof IntersectionObserver === 'undefined') {
      setInView(true);
      return;
    }
    const el = boxRef.current;
    if (!el) return;
    const io = new IntersectionObserver(entries => {
      if (entries.some(e => e.isIntersecting)) {
        setInView(true);
        io.disconnect();
      }
    }, {
      rootMargin: '300px'
    });
    io.observe(el);
    return () => io.disconnect();
  }, [inView]);
  if (status === 'missing') {
    if (!isDev) return null;
    return <div style={{
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'center',
      gap: '0.4rem',
      padding: '1rem 1.25rem',
      margin: '1.25rem 0',
      width: '100%',
      aspectRatio: ratio,
      boxSizing: 'border-box',
      border: '1px dashed rgba(128,128,128,0.45)',
      borderRadius: '0.75rem',
      background: 'rgba(128,128,128,0.06)',
      color: 'currentColor',
      fontSize: '0.85rem',
      lineHeight: 1.45
    }}>
        <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: '0.5rem',
      opacity: 0.75
    }}>
          <span aria-hidden="true">🎬</span>
          <code style={{
      fontSize: '0.75rem'
    }}>{id}</code>
          <span style={{
      fontSize: '0.65rem',
      textTransform: 'uppercase',
      letterSpacing: '0.04em',
      padding: '0.1rem 0.4rem',
      borderRadius: '0.4rem',
      background: 'rgba(128,128,128,0.18)'
    }}>
            {type}
          </span>
        </div>
        <div style={{
      opacity: 0.9
    }}>{caption || 'Media not uploaded yet.'}</div>
        <div style={{
      fontSize: '0.7rem',
      opacity: 0.5
    }}>
          Upload to R2 as <code>{id}.{resolvedExt}</code> — preview only, hidden in production.
        </div>
      </div>;
  }
  const mediaStyle = {
    display: status === 'loaded' ? 'block' : 'none',
    width: '100%',
    height: 'auto',
    borderRadius: '0.75rem'
  };
  const media = isVideo ? <video src={inView ? src : undefined} autoPlay loop muted playsInline preload="metadata" onLoadedData={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} /> : <img src={inView ? src : undefined} alt={caption} onLoad={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} />;
  return <figure style={{
    margin: '1.25rem 0'
  }}>
      <div ref={boxRef} style={status === 'loaded' ? {
    width: '100%'
  } : {
    width: '100%',
    aspectRatio: ratio,
    borderRadius: '0.75rem',
    background: 'rgba(128,128,128,0.06)'
  }}>
        {media}
      </div>
      {status === 'loaded' && caption ? <figcaption style={{
    marginTop: '0.5rem',
    textAlign: 'center',
    fontSize: '0.85rem',
    opacity: 0.7
  }}>
          {caption}
        </figcaption> : null}
    </figure>;
};

When more than one person opens the same workflow in the [Workflow Builder](/workflow-builder/overview), everyone edits the **same live canvas at the same time**. You see each other's cursors move, watch nodes appear and connect as your teammates add them, and know which node someone else is working on — all without anyone hitting "save" or stepping on each other's changes.

This page is the friendly tour of what live co-editing looks and feels like. If you want the engineering detail behind it, two companion pages go deeper:

<CardGroup cols={2}>
  <Card title="Realtime co-editing & external sync" icon="arrows-rotate" href="/workflow-builder/realtime-coediting">
    The builder mechanics: edit events, how changes broadcast, and how Composer or API changes flow into an open canvas.
  </Card>

  <Card title="Presence, locks & versioning" icon="lock" href="/realtime/presence-locks">
    The wire-level reference: every realtime event, payload, timing, and edge case behind co-editing.
  </Card>
</CardGroup>

<MediaEmbed id="MX-MEDIA-3420" type="app_video" caption={"Two teammates co-editing the same workflow canvas in realtime, with live cursors, presence avatars, and a node lock."} />

## What live co-editing gives you

<CardGroup cols={2}>
  <Card title="One shared canvas" icon="object-group">
    Everyone with the workflow open is editing the same graph. There are no separate copies to merge later.
  </Card>

  <Card title="Live cursors & presence" icon="users">
    See who else is in the workflow and where they are pointing, each in their own color.
  </Card>

  <Card title="Node locks" icon="lock">
    When a teammate is editing a node, you see it is being worked on so you do not both change it at once.
  </Card>

  <Card title="Automatic saving" icon="cloud-arrow-up">
    Edits are saved for you in the background. There is no save button to remember.
  </Card>
</CardGroup>

<Note>
  Co-editing is a **product feature of the app** — it works automatically whenever two people open the same workflow. There is nothing to turn on, and no API or SDK call is involved. It runs over a dedicated realtime connection that is separate from the [API](/api-reference/overview) you use to run workflows from code.
</Note>

## Who can co-edit

Live editing of a workflow canvas is available to the **owner** and **admin** roles in your [organization](/concepts/organizations-roles). The `member` role is retired and read-only: people without an editing role can still open the workflow and watch it change in realtime, but they cannot move, add, or edit nodes.

<CardGroup cols={2}>
  <Card title="Owner & admin" icon="pencil">
    Full editing: add, move, connect, configure, and delete nodes, with everything synced live to the rest of the room.
  </Card>

  <Card title="View-only" icon="eye">
    See the canvas, the live cursors, and every edit as it happens — but cannot make changes. A safe way to follow along or review.
  </Card>
</CardGroup>

For exactly which actions each role can take, see [Roles & permissions](/security/roles-permissions). Co-editing is part of your signed-in app session, so it uses your normal login — not an [API key](/api-reference/authentication).

## Presence: see who is here

Open a workflow that a teammate already has open and you appear to each other right away.

<Steps>
  <Step title="Everyone gets a color">
    Each person in the workflow is given a color when they connect. That color follows them everywhere — their cursor, their highlight on a node, and their avatar — so you can tell at a glance who is doing what.
  </Step>

  <Step title="Avatars show who is in the room">
    The people currently editing this workflow show up as a row of avatars. When someone opens or closes the workflow, the row updates for everyone.
  </Step>

  <Step title="Away status">
    If a teammate steps away and goes inactive, they are shown as **Away** rather than dropping off the canvas entirely, so you know they are still in the workflow but not actively editing. They come back automatically when they return.
  </Step>
</Steps>

<Frame caption="Presence avatars and a teammate's colored cursor on a shared canvas.">
  <MediaEmbed id="MX-MEDIA-3421" type="screenshot" caption={"A workflow canvas showing two presence avatars and a teammate's colored cursor hovering a node."} />
</Frame>

### Two kinds of cursor

You will see your teammates' pointers in two ways, and both are normal:

<CardGroup cols={2}>
  <Card title="A moving pointer" icon="arrow-pointer">
    As a teammate moves their mouse across the canvas, you see their colored cursor glide around in realtime. This is purely visual — it is not saved anywhere.
  </Card>

  <Card title="A node they are focused on" icon="crosshair">
    When a teammate hovers or focuses a specific node, that node is highlighted in their color so you can see exactly what they are looking at.
  </Card>
</CardGroup>

## Node locks: avoid editing the same node at once

To keep two people from changing the same node's settings at the same moment, ModuleX uses **node locks**.

<Steps>
  <Step title="Open a node and it becomes yours">
    When you open a node to edit it in the [Detail Panel](/workflow-builder/canvas), ModuleX reserves that node for you. Your teammates see it marked as being edited by you, in your color.
  </Step>

  <Step title="Teammates see it is taken">
    If a teammate tries to edit a node you already have open, they are told it is currently being edited by you, so they wait or pick something else instead.
  </Step>

  <Step title="The lock releases when you are done">
    When you finish with the node, the lock clears and the node is free for anyone again. Locks also release automatically — see below.
  </Step>
</Steps>

<AccordionGroup>
  <Accordion title="Locks release on their own">
    A node lock holds for up to **5 minutes** and refreshes while you keep working on the node. If you walk away or close your laptop without closing the node, the lock expires on its own so it never blocks a node forever. If you disconnect or leave the workflow, every node you had locked is released for everyone at once.
  </Accordion>

  <Accordion title="A lock is a courtesy, not a hard wall">
    Node locks are designed to help people coordinate, not to forcibly stop edits. They are the signal that says "someone is in here, give them a moment." The point is to keep two people from quietly overwriting each other's work on the same node — so when you see a node is being edited by a teammate, give them room and come back to it.
  </Accordion>

  <Accordion title="View-only teammates still appear">
    People in a view-only role do not lock nodes (they are not editing), but they still show up in presence and you still see their cursor. They are watching, not editing.
  </Accordion>
</AccordionGroup>

## Your changes are saved automatically

There is no save button. As you and your teammates edit, ModuleX continuously saves the workflow for the whole room in the background and records the change in the workflow's [version history](/workflow-builder/versioning-history). When you reopen the workflow later — or a teammate opens it for the first time — they get the latest version, including everything the team just did.

<Note>
  Edits from the [AI Composer](/concepts/ai-composer) and other changes that happen outside the canvas also flow into everyone's open canvas in realtime. If a teammate asks the Composer to rebuild part of the workflow, you will see the new nodes appear on your canvas as well. The mechanics are covered in [Realtime co-editing & external sync](/workflow-builder/realtime-coediting).
</Note>

## What happens when two people edit at once

Most of the time, simultaneous edits just work — each person's change lands and everyone stays in sync. In the rare case where your view has fallen too far behind the live workflow (for example, after a brief network blip while a lot was changing), ModuleX detects the mismatch, tells you your changes conflicted, and **refreshes your canvas to the current version** so you are working from the latest state again.

<AccordionGroup>
  <Accordion title="Why a refresh instead of a merge">
    Refreshing to the live version guarantees everyone is looking at the same workflow, with no half-merged or duplicated nodes. If you had an edit in flight when the refresh happened, re-apply it on the freshly loaded canvas — it will take a moment, not a rebuild.
  </Accordion>

  <Accordion title="It is rare in normal use">
    The tolerance for being slightly behind is generous, so ordinary back-and-forth editing between teammates does not trigger a refresh. You will mostly notice this only after a connection interruption.
  </Accordion>

  <Accordion title="Avoid the same node at the same instant">
    The simplest way to never see a conflict is the everyday etiquette node locks encourage: when a teammate is editing a node, let them finish before you open the same one.
  </Accordion>
</AccordionGroup>

## Does co-editing cost credits?

No. Live canvas co-editing — being in a workflow together, seeing cursors and presence, locking nodes, and the automatic background saving — **does not use [credits](/billing/credits)** and does not count against your plan's usage limits. [Credits](/concepts/credits-billing) are charged when you actually run managed work: [running a workflow](/workflow-builder/execution/running), an [AI Composer](/concepts/ai-composer) turn, or an [Assistant](/assistant/overview) turn. Editing together is free.

## Tips for editing together

<CardGroup cols={2}>
  <Card title="Watch the cursors" icon="eye">
    A quick glance at where everyone's cursor is tells you who is working on which part of the workflow.
  </Card>

  <Card title="Respect node locks" icon="hand">
    If a node shows it is being edited by a teammate, work on a different part and circle back.
  </Card>

  <Card title="Use the Composer together" icon="wand-magic-sparkles">
    One person can ask the [AI Composer](/workflow-builder/composer) to draft a section while others refine it — Composer changes appear on every open canvas.
  </Card>

  <Card title="Bring people in view-only" icon="users-viewfinder">
    Reviewers and stakeholders can follow a build live in a view-only role without risk of changing anything.
  </Card>
</CardGroup>

## Related pages

<CardGroup cols={2}>
  <Card title="Realtime collaboration walkthrough" icon="play" href="/guides/realtime-collaboration">
    A step-by-step guide: invite a teammate and co-edit a workflow live.
  </Card>

  <Card title="Chat collaboration" icon="comments" href="/platform/collaboration/chat">
    Shared and organization-visible chats for working with the [Assistant](/assistant/overview) as a team.
  </Card>

  <Card title="Workflow builder overview" icon="diagram-project" href="/workflow-builder/overview">
    The visual canvas where co-editing happens.
  </Card>

  <Card title="Canvas & controls" icon="hand-pointer" href="/workflow-builder/canvas">
    Pan, zoom, select, the Detail Panel, and keyboard shortcuts.
  </Card>

  <Card title="Realtime & collaboration model" icon="wave-pulse" href="/concepts/realtime-model">
    How runs stream and how the canvas syncs, explained conceptually.
  </Card>

  <Card title="Roles & permissions" icon="user-shield" href="/security/roles-permissions">
    Which roles can edit a workflow and which are read-only.
  </Card>
</CardGroup>
