Remove tags from devices via API call

Hello Friends!

I’m making some device management with Python. It came to my attention that there is no “Remove Tag” API endpoint option.
Once applied tags to an device via API call, how do I remove them later on?

Here is my implementation how to apply tags:

    def apply_tag(self, tags):
        headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {self.pat}'
        }
        tag_list = list()
        for key, value in tags.items():
            tag_list.append({
                "name": key,
                "value": value
            })
        endpoint = f"{self.url}/api/management/v1/inventory/devices/{self.id}/tags"
        response = requests.patch(endpoint, \
                                headers=headers, \
                                data=json.dumps(tag_list), \
                                timeout=10)

        if response.status_code == 200: 
            return True
        else:
            print(f"Could not apply tags to device {self.id}, {self.serial}")
            print(f"HTTP response: {response.status_code}")
            print(f"API text: \n {response.text}")
            print(f"API reason: \n {response.reason}")
            return False

Can I utilise “Assign Tags” to remove tags?
What is the difference between “Assign” and “Apply”?

Thanks Everyone!

Hello @mister_kanister,

You are right. Assign (PUT) will overwrite everything (including the removal of not provided tags) while Add (PATCH) will append for new tags and replace for old ones. An empty Assign will basically remove all of the tags.
I think you meant “add” instead of “apply”.

Have a nice day!
Luis

Thanks for your reply! It brings some clarification. I still don’t get how this works. How must the header look like if I want remove every tag I passed in?
My sample:

    def remove_tags(self, tags):
        # tags = ["my_tag1", "second_tag"]
        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'If-Match': '<WHAT DO I PUT HERE?>',
            'Authorization': f'Bearer {self.pat}'
        }
        endpoint = f"{self.url}/api/management/v1/inventory/devices/{self.id}/tags"
        response = requests.put(endpoint, headers = headers, timeout=10)
        if response.status_code == 200: 
            return True
        else:
            print(f"Could not apply tags to device {self.id}, {self.serial}")
            print(f"HTTP response: {response.status_code}")
            print(f"API text: \n {response.text}")
            print(f"API reason: \n {response.reason}")

Looked up the field usage of If-Match for the HTTP header, still have no idea.

The API call and its handling is confusing. It removes all tags not listed in the tags list. It adds tags previously not set. If you want to remove all tags you need at least provide one last tag with empty value that was previously there. To remove a specific tag, you will need an API call to get a device and its inventory, get the tag list from that device, modify it and create another API call where you keep all expect the one you don’t want. I really would wish for a simpler solution.

The only variation I can get working is this. Precondition is the tag “owner” was previously there:

tags_list = [
        {
            "name": "owner",
            "value": ""
        }
]
def remove_tags(self, tags_list):  
        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Authorization': f'Bearer {self.pat}'
        }
        endpoint = f"{self.url}/api/management/v1/inventory/devices/{self.id}/tags"
        response = requests.put(endpoint, headers = headers, timeout=10, json=tags_list)
        if response.status_code == 200: 
            return True
        else:
            print(f"Could not apply tags to device {self.id}, {self.serial}")
            print(f"HTTP response: {response.status_code}")
            print(f"API text: \n {response.text}")
            print(f"API reason: \n {response.reason}")

I still dont understand what Etags are and how to use them.