> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.resemble.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.resemble.ai/_mcp/server.

# Custom Categories

Custom categories let a team define its own fraud patterns from example phrases, called scenarios. Once a category is ready, it is scored alongside the built-in catalog — or instead of it, if the team has disabled the built-ins with [Team Settings](/detect/signal/settings).

## Embedding lifecycle

Categories embed asynchronously after creation. A category is only used for scoring once its status is `ready`.

| Status                | Meaning                                   |
| --------------------- | ----------------------------------------- |
| `pending`             | Created; embedding has not started.       |
| `embedding`           | Example phrases are being embedded.       |
| `ready`               | Available for scoring.                    |
| `needs_more_examples` | Too few scenarios to score reliably.      |
| `failed`              | The embed job errored. See `embed_error`. |

## List categories

Returns the team's settings, the built-in categories, the team's custom categories with their scenarios and overlap warnings, and calibration status.

`GET https://app.resemble.ai/api/v2/signal/custom_categories`

```bash
curl --request GET 'https://app.resemble.ai/api/v2/signal/custom_categories' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

```json
{
  "success": true,
  "settings": { "use_builtin_categories": true },
  "built_in_categories": [
    {
      "id": 7,
      "name": "CEO Impersonation / Wire Diversion",
      "icon": "👔",
      "scenario_count": 24,
      "example_texts": ["transfer the funds immediately", "send the wire today"]
    }
  ],
  "custom_categories": [
    {
      "id": 99,
      "name": "Tech Support Scam",
      "status": "ready",
      "enabled": true,
      "scenario_count": 2,
      "overlaps": [],
      "scenarios": [
        { "id": 501, "text": "Your computer has a virus, press 1 to continue", "embedded": true }
      ]
    }
  ],
  "calibration": {
    "status": "completed",
    "calibrated_at": "2026-06-14T10:00:00.000Z",
    "overall_recall": 0.94,
    "warnings": []
  }
}
```

`calibration` is `null` until the team's categories have been calibrated. `overlaps` lists calibration warnings where this category was confused with another one.

## Create a category

`POST https://app.resemble.ai/api/v2/signal/custom_categories`

### Request parameters

| Field         | Type      | Required | Description                                                                                                                   |
| ------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `name`        | string    | Yes      | Category name. Must be unique within the team.                                                                                |
| `scenarios`   | string\[] | Yes      | Example phrases that define the pattern. Maximum 50, each up to 2000 characters. A newline-separated string is also accepted. |
| `description` | string    | No       | Free-form description.                                                                                                        |
| `icon`        | string    | No       | Single emoji.                                                                                                                 |
| `enabled`     | boolean   | No       | Whether to include the category in scoring. Defaults to `true`.                                                               |

```bash
curl --request POST 'https://app.resemble.ai/api/v2/signal/custom_categories' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  --data '{
    "name": "Tech Support Scam",
    "icon": "☎️",
    "description": "Calls impersonating tech support",
    "scenarios": [
      "Your computer has a virus, press 1 to continue",
      "We detected suspicious activity, verify your identity now"
    ]
  }'
```

### Response

```json
{
  "success": true,
  "item": {
    "id": 99,
    "name": "Tech Support Scam",
    "description": "Calls impersonating tech support",
    "icon": "☎️",
    "enabled": true,
    "status": "pending",
    "embed_error": null,
    "embedded_at": null,
    "scenario_count": 2,
    "embedding_model_version": null,
    "overlaps": [],
    "scenarios": [
      { "id": 501, "text": "Your computer has a virus, press 1 to continue", "embedded": false },
      { "id": 502, "text": "We detected suspicious activity, verify your identity now", "embedded": false }
    ]
  }
}
```

### Response fields

| Field                          | Type           | Description                                                             |
| ------------------------------ | -------------- | ----------------------------------------------------------------------- |
| `item.id`                      | integer        | Category identifier.                                                    |
| `item.name`                    | string         | Category name.                                                          |
| `item.description`             | string \| null | Free-form description.                                                  |
| `item.icon`                    | string \| null | Single emoji.                                                           |
| `item.enabled`                 | boolean        | Whether the category is included in scoring.                            |
| `item.status`                  | string         | Embedding status. See [Embedding lifecycle](#embedding-lifecycle).      |
| `item.embed_error`             | string \| null | Error message when `status` is `failed`.                                |
| `item.embedded_at`             | string \| null | When embedding completed.                                               |
| `item.scenario_count`          | integer        | Number of example phrases.                                              |
| `item.embedding_model_version` | string \| null | Model version used to embed the scenarios.                              |
| `item.overlaps`                | array          | Calibration warnings where this category was confused with another.     |
| `item.scenarios`               | array          | Example phrases, each with `id`, `text`, and whether it has `embedded`. |

Validation errors return `422 Unprocessable Entity`:

```json
{
  "success": false,
  "message": "Too many scenarios — max 50"
}
```

## Get a category

`GET https://app.resemble.ai/api/v2/signal/custom_categories/{id}`

```bash
curl --request GET 'https://app.resemble.ai/api/v2/signal/custom_categories/99' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

Returns the same `item` shape as create. Unknown identifiers return `404 Not Found`.

## Update a category

`PATCH https://app.resemble.ai/api/v2/signal/custom_categories/{id}`

Accepts any subset of the create parameters.

```bash
curl --request PATCH 'https://app.resemble.ai/api/v2/signal/custom_categories/99' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  --data '{ "enabled": false }'
```

> **Warning**
>
> Supplying `scenarios` **replaces** the entire example set and re-triggers embedding. The category returns to `pending` until embedding completes.

## Delete a category

`DELETE https://app.resemble.ai/api/v2/signal/custom_categories/{id}`

```bash
curl --request DELETE 'https://app.resemble.ai/api/v2/signal/custom_categories/99' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

```json
{
  "success": true
}
```