---
title: "Keyword groups"
description: "Group keywords by customer, campaign or product, track the same term in more than one group, and read what each group cost."
canonical: https://docs.mentio.dev/groups
markdown: https://docs.mentio.dev/groups.mdx
---

# Keyword groups

Group keywords by customer, campaign or product, track the same term in more than one group, and read what each group cost.

A group is how a workspace groups its keywords: one per customer, per campaign, per product, whatever you count by. Every keyword belongs to exactly one group. A term is unique per group, not per workspace, so two groups may track "bloom" as two keywords, each matched, capped and billed on its own.

Every workspace has a **default group**, created with it. A keyword created without a `groupId` lands there. The default group can be renamed and never deleted.

Groups exist for one job: running one Mentio workspace on behalf of several of your own customers, where two of them want the same term with different rules, different caps and separate bills. A workspace tracking its own brand needs none of this and never sees it: the dashboard shows groups only once a second one exists.

## Create a group and put keywords in it

`name` is unique per workspace. `externalId` is optional and unique too: your own id for the group, a customer id say, so you can find it again with `GET /v1/groups?externalId=` instead of storing ours.

```bash
curl -X POST https://api.mentio.dev/v1/groups \
  -H "Authorization: Bearer $MENTIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Bloom Coffee", "externalId": "cust_8231" }'
```

```json
{
  "id": "grp_7f3a...",
  "name": "Bloom Coffee",
  "externalId": "cust_8231",
  "isDefault": false,
  "stats": { "keywords": 0, "active": 0 },
  "createdAt": "2026-09-24T10:00:00.000Z",
  "updatedAt": "2026-09-24T10:00:00.000Z"
}
```

Then create the keyword with its `groupId`:

```bash
curl -X POST https://api.mentio.dev/v1/keywords \
  -H "Authorization: Bearer $MENTIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "term": "bloom", "kind": "brand", "groupId": "grp_7f3a...", "cap": { "mentions": 500 } }'
```

A second `bloom` in the same group is a `409 duplicate_keyword`; a `bloom` in another group is a new keyword. `PATCH /v1/keywords/{id}` with `groupId` moves a keyword between groups, with the same 409 when the target already tracks the term.

## A company description per group

The classifier judges every mention against "the company": by default the workspace's company profile. When you run one workspace for several businesses, that profile is yours, not theirs, and a keyword's `context` (a 300-character instruction) cannot stand in for it. A group can carry its own description in `context`: who the business is, what it sells, for whom, what is not it. The classifier reads it in place of the whole workspace profile for every keyword in the group: the profile's description, relevance guidelines, competitor list and own accounts all give way, so a rule that should hold for the group ("ignore job posts") goes in the group's text. A second keyword on the same post reuses a sibling's verdict only when that sibling was judged as the same company (Mentio records which on the match: the workspace profile, or the group's description), so a keyword moved between groups, a description changed or cleared, or a group renamed all do the right thing without rescoring anything. Editing the workspace profile changes nothing about reuse, as before. Null means the workspace profile, as before. The default group takes no description: it is the workspace itself, and its company is the profile.

```bash
curl -X PATCH https://api.mentio.dev/v1/groups/grp_7f3a... \
  -H "Authorization: Bearer $MENTIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "context": "Bloom Coffee roasts specialty coffee in Copenhagen and sells beans and subscriptions online. Not the Bloom fintech app, not the flower shop." }'
```

New mentions are judged with the new description at once; old ones are not rescored.

## Where the group shows

* Every keyword carries `group` (`id`, `name`, `externalId`, `isDefault`), and `GET /v1/keywords?groupId=` lists one or more groups' (comma-separated, like the list's other filters).
* Every mention carries `keyword.group`, so a webhook payload says which customer's keyword matched. `GET /v1/mentions?groupIds=` (comma-separated, any-of) narrows the list to one or more groups, `notGroupIds` leaves groups out, and the CSV export carries `group` and `group_external_id` columns.
* An alert rule's filter takes `groupIds`: one rule per customer, delivering to that customer's channel, whatever its keywords are on the day.
* The analytics reports name the group on every keyword row.
* In digests and alert emails a keyword outside the default group reads as `bloom (Bloom Coffee)`.

## What a group cost

`GET /v1/usage/breakdown?by=group` is the bill per group: the keyword-days charged for its keywords, the mentions they matched and billed, and the sum, at list price. Keyword-days are counted under the group the keyword was in on the day they were charged, so moving or deleting a keyword never moves money that was already spent. A group deleted during the window keeps its row, marked `group.removed`.

```bash
curl "https://api.mentio.dev/v1/usage/breakdown?by=group&month=2026-09" \
  -H "Authorization: Bearer $MENTIO_API_KEY"
```

## Deleting a group

`DELETE /v1/groups/{id}` deletes the group and every keyword in it, each the way `DELETE /v1/keywords/{id}` does: its mentions go with it, alert rules that named it are adjusted, charges already made stay on the usage record. It answers 204 like every delete; read the group first if you want to know how many keywords go (`stats.keywords`). The default group cannot be deleted: move or delete its keywords instead.
