On this page
Guides / Contact tags

Contact tags

Manage the CRM's tag catalog, add and remove tags on one contact or hundreds, and filter your base by them.

Updated View .md

Tags are the fastest way to segment the CRM: "cliente", "lead-caliente", "Participante". In Met they live in two places, and it pays to keep them apart:

Reading requires contacts:read; writing, contacts:write. Both work with a test key. Routes are flat (/contact-tags, /contacts/…): they operate on the key's workspace.

The catalog

const tags = await met.contactTags.list();
// [{ id: 74, name: 'lead-caliente' }, { id: 75, name: 'Participante' }]

const vip = await met.contactTags.create({ name: 'VIP' });
await met.contactTags.rename(vip.id, 'Cliente VIP');
await met.contactTags.delete(vip.id); // { ok: true }
tags = met.contact_tags.list()

vip = met.contact_tags.create({"name": "VIP"})
met.contact_tags.rename(vip["id"], "Cliente VIP")
met.contact_tags.delete(vip["id"])
curl -X POST https://api.met.meteor.com.co/api/v1/contact-tags \
  -H "Authorization: Bearer $METEOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"VIP"}'

Adding and removing a contact's tags

await met.contacts.addTag(4821, 'lead-caliente');
await met.contacts.removeTag(4821, 'frío');

const theirs = await met.contacts.tags(4821); // ['lead-caliente', 'Participante']
met.contacts.add_tag(4821, "lead-caliente")
met.contacts.remove_tag(4821, "frío")

theirs = met.contacts.tags(4821)

Adding a tag to a contact that is not in the catalog registers it in the catalog on its own. Adding one the contact already has does nothing.

Many contacts at once

One tag on a list of contacts, in a single call (up to 500 per call):

const r = await met.contacts.bulkAddTags({ contact_ids: [4821, 4822, 4830], tag: 'Feria 2026' });
// { updated: 2, requested: 3, tag: 'Feria 2026' }

await met.contacts.bulkRemoveTags({ contact_ids: [4821, 4822], tag: 'Feria 2026' });
r = met.contacts.bulk_add_tags({"contact_ids": [4821, 4822, 4830], "tag": "Feria 2026"})
met.contacts.bulk_remove_tags({"contact_ids": [4821, 4822], "tag": "Feria 2026"})

updated only counts the contacts that actually changed: those that already had the tag, or ids that are not contacts of this workspace, do not add up. It is one tag per call (tag, not a list).

Filtering by tag

contacts.list() accepts conditions on the contact's fields in filters, a JSON array. For tags, the field is $tags and the operators are has and not_has:

const { data, total } = await met.contacts.list({
  filters: JSON.stringify([{ field: '$tags', op: 'has', value: 'lead-caliente' }]),
});
import json

page = met.contacts.list(filters=json.dumps([{"field": "$tags", "op": "has", "value": "lead-caliente"}]))

A malformed condition is ignored instead of raising an error: check total before trusting the filter. To know which tags exist, adding up the catalog's and those contacts have even if they are not registered, use met.contacts.tagsDistinct().

Hearing about changes

There is no tag event of its own. Adding or removing a tag on a contact changes the contact, and that goes out as contact.updated (scope contacts:read; see Webhooks).

Common errors

CodeWhat happened
400 validation_errorThe name is empty or longer than 60 characters; in bulk, more than 500 contacts or an empty list.
404 not_foundThe tag (in rename or delete) or the contact does not exist in this workspace.
409 conflictA tag with that name already exists, ignoring case.
403 missing_scopeThe key does not carry contacts:write (or contacts:read for reading).