Stop Failing at Monday.com Connect Boards (The JSON Fragment Fix)

Fix the silent failures when linking items between boards using the Monday.com API.

If you are trying to link items between boards using the Monday.com API, you have probably hit the "Silent Failure" wall. You send a mutation, it returns 200 OK, but the items simply... don't link.

Or worse, you try to read the linked items, and you get back... nothing. Just an empty string.

This isn't your fault. The official documentation is famously vague about the specific GraphQL Fragments required for board_relation columns.

Here is the architecture I use to handle Bidirectional Linking reliably.

The Problem: The "Generic" Value Trap

Monday.com treats most columns (Text, Numbers) as simple strings. But a board_relation (Connect Boards) column is a complex object.

If you try to read it like this:

graphqlquery { items (ids: [12345]) { column_values { text value } } }

You will get a useless JSON string. The API hides the real data (the Item IDs) behind a Fragment.

The Solution: The BoardRelationValue Fragment

To actually see what is linked, you must tell GraphQL you expect a BoardRelationValue.

rubyquery = <<~GRAPHQL query { items (ids: [12345]) { column_values { ... on BoardRelationValue { display_value linked_item_ids } } } } GRAPHQL
pythonquery = """ query { items (ids: [12345]) { column_values { ... on BoardRelationValue { display_value linked_item_ids } } } } """
javascriptconst query = ` query { items (ids: [12345]) { column_values { ... on BoardRelationValue { display_value linked_item_ids } } } } `;

Why this matters: Without ... on BoardRelationValue, you are flying blind. You cannot verify if your links actually persist.

The Write Fix: change_multiple_column_values

When Updating links, you can't just send an array of IDs. You need a specific JSON structure wrapped in the change_multiple_column_values mutation.

rubydef link_items(board_id, item_id, column_id, target_item_ids) # Structure: {"linkedPulseIds": [{"linkedPulseId": 123}, ...]} value_hash = { linkedPulseIds: target_item_ids.map { |id| { linkedPulseId: id.to_i } } } query = <<~GRAPHQL mutation { change_multiple_column_values( item_id: #{item_id}, board_id: #{board_id}, column_values: #{value_hash.to_json.inspect} ) { id } } GRAPHQL request(query) end
pythonimport json def link_items(board_id, item_id, column_id, target_item_ids): # The Payload MUST be a JSON string inside the GraphQL query # Structure: {"linkedPulseIds": [{"linkedPulseId": 123}, ...]} value_obj = { "linkedPulseIds": [{"linkedPulseId": int(id)} for id in target_item_ids] } # JSON dump once to get the string: '{"linkedPulseIds":...}' # JSON dump again to escape it for GraphQL: '"{"linkedPulseIds":...}"' json_string = json.dumps(json.dumps(value_obj)) mutation = f""" mutation {{ change_multiple_column_values( item_id: {item_id}, board_id: {board_id}, column_values: {json_string} ) {{ id }} }} """ return request(mutation)
javascriptasync function linkItems(boardId, itemId, columnId, targetItemIds) { const valueObj = { linkedPulseIds: targetItemIds.map(id => ({ linkedPulseId: parseInt(id) })) }; // Convert to JSON string const jsonString = JSON.stringify(valueObj); // JSON.stringify AGAIN to escape for GraphQL string const escapedJsonString = JSON.stringify(jsonString); const mutation = ` mutation { change_multiple_column_values( item_id: ${itemId}, board_id: ${boardId}, column_values: ${escapedJsonString} ) { id } } `; return await request(mutation); }

Key "Gotchas"

  1. Integers Only: The linkedPulseId must be an Integer. Sending a string ID here will often fail silently.
  2. The Double Wrap: Note value_hash.to_json.inspect. You are sending a JSON string inside a GraphQL query. If you don't escape it correctly, the parser explodes.
  3. Idempotency: This mutation overwrites existing links. If you want to add a link, you must first Read (using the Fragment!), merge the arrays, and then Write.

Architecture Pattern

For my "Monday Expert" PMO system, I wrap this logic in a LinkerService. It never assumes a link exists. It always:
1. Reads the BoardRelationValue.
2. Diffs the IDs.
3. Mutates only if necessary.

This prevents the "API Complexity Limit" errors (covered in The Missing Manual for Monday.com GraphQL Complexity) by avoiding redundant calls.


This is part of the "Universal PMO Architecture" series.


Related Reading

Found this helpful? Share it with your network:

Written by Rick Apichairuk

Founder, Monday Expert

Systems designer focused on building clear, scalable Monday.com architectures. Writes about board design, data modeling, and operational patterns used in real teams.

Apply for a Live Build Session

Get your Monday.com workflow built live on stream. Real solutions, real learning, completely free.

Apply Now