Monday.com API Convert Items to Sub Items

Looking for some aid or suggested alternate solution on how to use the API to move/convert an item within a group, to a subitem within the same group.

Have:

Wanted:

Current Workflow:

I have been successful with taking the name of the group and creating an additional item within its group. For example:

I have also been successful with creating the subitem in Group One, however, the columns do not transfer over. I’d like to avoid this methodolgy since it requires me to first query and format the group data to be fed into “create_subitems”. Seems like a very redundent method. (Note, the printed column_values in the code do not match the picture. Just an example).

def create_subitem_for_item(item_id, sub_item_name, column_values):
    query = """
    mutation ($itemId: ID!, $subItemName: String!, $columnValues: JSON) {
        create_subitem (
            parent_item_id: $itemId,
            item_name: $subItemName,
            column_values: $columnValues
            create_labels_if_missing: true
            ) {
                id
        }
    }   
    """

    variables = {
        'itemId': item_id,
        'subItemName': sub_item_name,
        'columnValues': json.dumps(column_values, separators=(',', ':'))
    }

    datas = {
        'query': query,
        'variables': variables
    }

    r_boards = requests.post(url=apiUrl, headers=headers, data=json.dumps(datas)) # make request
    print(variables)
    print(r_boards.json())
    return r_boards

>>>
{'itemId': '123', 'subItemName': 'Item 1', 'columnValues': '{"text1":"\\"Some Text\\"","text2":"\\"Some More Text\\"","number1":"\\"200\\"","number2":"\\"12541.9\\""}'}
{'data': {'create_subitem': {'id': '1234'}}, 'account_id': 56789}

Question 1:
I know in monday.com you can select the item row and then “convert” into a subitem. Is there a way to do that through the API?

Question 2:
If there is no API methodology, how do I create a subitem with desired columns and it’s respective values? It seems that I am building out the column_values correctly within create_subitem, yet the columns nor the values are being built.

“”“”
Let me know if you need additional information/examples.

It looks like you may be using the column titles instead of column ID? column Ids are always lowercase and your column IDs are “Text1” and “Numbers2” for example instead of “text1” and “numbers2”. So its just not finding the columns possibly?

1 Like

Apologies and thanks for catching that, but in my data I do have them as lowercase. Just updated my post to reflect that. I doubled checked my data and I can confirm those are the column IDs.

1 Like

It seems odd to me, there seems to be too many " marks around the values in column values. Like every string includes the quotes. Which may cause errors with number columns.

What I do know is your query is correct, so the issue is in the formatting of the values. My only thought is your strings going into the have quotes in them and they shouldn’t - particularly the numbers.

I fabricated some better data. Maybe that will help shed some better light. Also, here is some more code on how I am constructing the data to be fed into the create_sub_item function I have above. This run shows that the sub item title wasn’t constructed at all, but a subitem was in fact created. Sometimes the title is constructed and sometimes not.

Strangely, every time I create a sub item, I get one Internal server error, status code == 500 message. That is the purpose for my while loop below. It only happens once, so if I were to not sys.exit() out, the other two groups would not encounter that error.

# Query each group and create a subitem for item
query_group_id = f"""
{{
    boards (ids: {dup_board_id}) {{
        groups {{
            title
            items_page (limit: 50) {{
                cursor
                items {{
                    id
                    name
                    column_values {{
                        id
                        value
                    }}
                }}
            }}
        }}
    }}
}}
"""
data = {'query' : query_group_id}
r = requests.post(url=apiUrl, json=data, headers=headers)
r_dict = r.json()
print(r_dict)
group_board_info = r_dict['data']['boards'][0]['groups']
print(group_board_info)

# Iterate through each group, skipping the first item. First item is skipped since that will be the "parent item"
# Copy column values and names to be created as a subitem for the parent item (relative to that group)
for dictionary in group_board_info:
    print(dictionary)
    items = dictionary['items_page']['items']
    print(items)

    for cnt, item in enumerate(items):
        if cnt == 0:
            parent_item_id = item['id']
            continue
        
        cv = item['column_values']
        subitem_name = item['name']
        column_value_dict = {}

        for d in cv:
            column_value_dict[d['id']] = d['value']

        print(item)
        print(parent_item_id)
        print(cv)
        print(subitem_name)
        print(column_value_dict)

        # Internal server error (unknown)
        run_again = True
        while run_again:
            r = create_subitem_for_item(parent_item_id, subitem_name, column_value_dict)
            if r.status_code == 500:
                time.sleep(1)
                print('Internal server error found')
            else:
                run_again = False
        sys.exit()

Print Outputs

>>>
r_dict:
{'data': {'boards': [{'groups': [{'title': 'Home - Mortgage - Oceans 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Oceans 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': 'Imagery', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Work"'}, {'id': 'compensation', 'value': '"0"'}]}]}}, {'title': 'Home - Mortgage - Ground 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Ground 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': '2000.1 - 2000.1 Vegetation', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Hours"'}, {'id': 'compensation', 'value': '"0"'}]}]}}, {'title': 'Home - Mortgage - Mountains 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Mountains 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': 'Detection', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial"'}, {'id': 'compensation', 'value': '"0"'}]}]}}, {'title': 'Group Title', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Task 1', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}]}}]}]}, 'account_id': 12345}

group_board_info:
[{'title': 'Home - Mortgage - Oceans 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Oceans 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': 'Imagery', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Work"'}, {'id': 'compensation', 'value': '"0"'}]}]}}, {'title': 'Home - Mortgage - Ground 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Ground 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': '2000.1 - 2000.1 Vegetation', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Hours"'}, {'id': 'compensation', 'value': '"0"'}]}]}}, {'title': 'Home - Mortgage - Mountains 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Mountains 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': 'Detection', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial"'}, {'id': 'compensation', 'value': '"0"'}]}]}}, {'title': 'Group Title', 'items_page': {'cursor': None, 'items': [{'id': '5597071563', 'name': 'Task 1', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}]}}]

dictionary:
{'title': 'Home - Mortgage - Oceans 2023', 'items_page': {'cursor': None, 'items': [{'id': '12345', 'name': 'Oceans 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': 'Imagery', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Work"'}, {'id': 'compensation', 'value': '"0"'}]}]}}

items:
[{'id': '12345', 'name': 'Oceans 2023', 'column_values': [{'id': 'phase', 'value': None}, {'id': 'compensation', 'value': None}]}, {'id': '12345', 'name': 'Imagery', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Work"'}, {'id': 'compensation', 'value': '"0"'}]}]

item:
{'id': '12345', 'name': 'Imagery', 'column_values': [{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Work"'}, {'id': 'compensation', 'value': '"0"'}]}

parent_item_id:
12345

cv:
[{'id': 'phase', 'value': '"1000 - 1000 Geospatial : Work"'}, {'id': 'compensation', 'value': '"0"'}]

subitem_name:
Imagery

column_value_dict:
{'phase': '"1000 - 1000 Geospatial : Work"', 'compensation': '"0"'}

function variables:
{'itemId': '12345', 'subItemName': 'Imagery', 'columnValues': '{"phase":"\\"1000 - 1000 Geospatial : Work\\"","compensation":"\\"0\\""}'}

function .json():
{'data': {'create_subitem': {'id': '12345'}}, 'account_id': 12345}

Hello there @Binx96,

I am not experienced with Python, but your mutation does look correct.

Something like this is valid:

mutation ($itemId: ID!, $subItemName: String!, $columnValues: JSON) {
        create_subitem (
            parent_item_id: $itemId,
            item_name: $subItemName,
            column_values: $columnValues
            create_labels_if_missing: true
            ) {
                id
        }
    }
        {
        "itemId": 1234567890,
        "subItemName": "Some name",
        "columnValues": "{\"text\":\"Some text\", \"numbers\":\"100.1\"}"
        }

Postman translates this to Python as:

import http.client
import json

conn = http.client.HTTPSConnection("api.monday.com")
payload = "{\"query\":\"mutation ($itemId: ID!, $subItemName: String!, $columnValues: JSON) {\\n        create_subitem (\\n            parent_item_id: $itemId,\\n            item_name: $subItemName,\\n            column_values: $columnValues\\n            create_labels_if_missing: true\\n            ) {\\n                id\\n        }\\n    } \",\"variables\":{\"itemId\":1234567890,\"subItemName\":\"Some name\",\"columnValues\":\"{\\\"text\\\":\\\"Some text\\\", \\\"numbers\\\":\\\"100.1\\\"}\"}}"
headers = {
  'API-Version': '2023-10',
  'Authorization': 'APITOKENHERE',
  'Content-Type': 'application/json',
}
conn.request("POST", "/v2/", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

I hope that helps!

Cheers,
Matias

1 Like

It looks like all the strings you have for values have " around them within the strings themselves - even for the numbers.

1 Like

Yeah I am seeing that too. I am not manipulating those at all though. They are coming straight from querying the board.

1 Like

Thanks Matias, I’ll look through this later this week and report back my findings.

2 Likes

@Matias.Monday

Looked through your mutation, but still no luck. I copied your postman example and replaced it with my ids and data. Columns and values were still not being populated. The default columns when creating a subitem were only present.

I’ve also tried running it straight from the playground. Same results as above (item names are populated, but columns/column values do not change).

I am running out of ideas. Should I submit a ticket to your IT support?

import requests

apiKey = '...'
apiUrl = "https://api.monday.com/v2"
headers = {"Authorization" : apiKey,
        "Content-Type" : 'application/json',
        "API-Version" : '2023-10'}

# My payload
payload = "{\"query\":\"mutation ($itemId: ID!, $subItemName: String!, $columnValues: JSON) {\\n        create_subitem (\\n            parent_item_id: $itemId,\\n            item_name: $subItemName,\\n            column_values: $columnValues\\n            create_labels_if_missing: true\\n            ) {\\n                id\\n        }\\n    } \",\"variables\": {\"itemId\": 12345,\"subItemName\":\"Imagery\",\"columnValues\":\"{\\\"phase\\\":\\\"1000 - 1000 Geospatial\\\", \\\"compensation\\\":\\\"0\\\"}\"}}"

# Your payload example
# payload = "{\"query\":\"mutation ($itemId: ID!, $subItemName: String!, $columnValues: JSON) {\\n        create_subitem (\\n            parent_item_id: $itemId,\\n            item_name: $subItemName,\\n            column_values: $columnValues\\n            create_labels_if_missing: true\\n            ) {\\n                id\\n        }\\n    } \",\"variables\":{\"itemId\":1234567890,\"subItemName\":\"Some name\",\"columnValues\":\"{\\\"text\\\":\\\"Some text\\\", \\\"numbers\\\":\\\"100.1\\\"}\"}}"


r_boards = requests.post(url=apiUrl, headers=headers, data=payload)
print(r_boards.json())

Monday.com Playground

mutation {
  create_subitem (
    parent_item_id: 12345
    item_name: "Test_Name"
    column_values: "{\"phase\":\"1000 - 1000 Geospatial : Work\", \"compensation\": \"0\"}"
    create_labels_if_missing:true
  ) {
    id
  }
}

@Matias.Monday

Maybe there is some miscommunication with what I am trying to accomplish. I have been looking around at other posts and it seems that I may need to create a initial subitem first, and then create columns within that subitem. Then I can use my current code to fill in the values. However, I’ve read that the API does not support creating columns within a subitem. Is this still true?

You need to query the settings_str of boards{columns(ids: "subitems"){settings_str}}, do json.dumps on that and you’ll get an object that includes boardIds an array of integers. It will only contain one board ID, that of the subitems board.

You can then do a regular create column mutation on the subitems board to create a column.

1 Like

Thanks @codyfrisch! There was some extra code I had to put together to get everything to run smoothly. TLDR, that did the trick. Thank you for pointing that out!

Thank you @codyfrisch for the help!

Quick question, because subitems are a board, can I use move_item_to_board. Here I am creating a subitem, but does move item support moving an item to a subitem? I tried it in the api playground and ran into a Internal Server Error. Before I continue digging, want to make sure I can do that before I waste too much time.

Not a clue to be honest.

@Matias.Monday, do you know if this can be done?

Hi @Binx96,

Thanks for following up.

The team just confirmed that it is not possible as of now, and there are no immediate plans to implement it…but I’ve opened a feature request and added your vote to it :smiley:

Also, do you mind sharing your use case for the request?

Best,
Rachel

1 Like

Hi @rachelatmonday ,

Thanks for confirming. Absolutely I can share!

Lets use my original post as an example. Image one is a board that contains multiple groups with a range of 1-50 items. The second image is another board with one group. Board_2 items will be the name of each group from board_1. The subitems of each item are the items from board_1 in respect to their group. Image 2 illustrates that better.

My current solution:

  1. Duplicate board_1
  2. Create the “Active Projects” group
  3. Create an “initiate” subitem to allow adding and removing of it’s columns
  4. For each group:
    a. Create an item of the group name
    b. Create an subitem for each item in the group
    c. Delete the item once it’s subitem is created
    d. Once all subitems are created, move the item (with subitems) up to “Active Projects”
    e. Delete group

Wanted solution:

  1. Create new board
  2. Create “Active Projects” group
  3. Create columns/types
  4. Create items based on group names from board_1 (already built in my code).
  5. Move the items from board_1 to subitems in board_2 in respect to their group

When working with a small dataset, current solution works just fine. However, when working with several thousands of items. Each create, delete, move step takes a few seconds. Moving an item to a specific location takes significantly less time (my hope) then duplicating it, re-creating it as a subitem, deleting the original and moving it to another group.

Subitems already has it’s own board id, so I would think it would be trivial to be able to move an item between two boards so long as they have the same columns.

Hope that makes sense and feel free to ask any clarifying questions.

@Binx96 thanks for the context! It’s always beneficial to include a use case/as many details as possible for requests :slight_smile:

I’ve added all of this to the feature request, and we will be sure to update here in the community and the changelog if/when it’s resolved.

Best,
Rachel

2 Likes