# Features tools

> MCP tools for Features: features_create, features_list, features_update.

- `features_create`: Create a feature
- `features_list`: List features
- `features_update`: Update a feature

## `features_create`

**Create a feature.** Operation [`features.create`](https://ownrecord.io/docs/reference/features-create.md) · Brand level · permission `catalog:write` · risk tier `write` · since M4

Defines a feature of the brand, in the request's mode: something a product grants, such as `feature.export`
(a boolean), `seats.max` (a limit) or `support.level` (one of a set of values). Products grant values of it
in their `entitlements`, and customers' entitlements are resolved from what they've bought.

`combine` says how values from several products merge: `or` or `first` for booleans, `max`, `sum` or `first`
for limits, `first` for enums. The key and type never change, and enum values can be added but not removed.
A `perUnit` limit is multiplied by the subscription item's or purchase's quantity.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type                               | Required | Description                                                                                                                                                                                              |
| ----------------- | ---------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`             | string                             | yes      | Unique in the brand and mode, e.g. `seats.max`. Never changes. (at most 100 characters)                                                                                                                  |
| `name`            | string                             | yes      | Shown on the pricing table, e.g. Seats. (1–120 characters)                                                                                                                                               |
| `type`            | one of `boolean`, `limit`, `enum`  | yes      | `boolean` (on or off), `limit` (a whole number) or `enum` (one of a set of values).                                                                                                                      |
| `enumValues`      | array of string                    | no       | Enum features: the allowed values, in display order. (1–50 items)                                                                                                                                        |
| `combine`         | one of `max`, `sum`, `or`, `first` | no       | Default: `or` for booleans, `max` for limits, `first` for enums.                                                                                                                                         |
| `defaultValue`    | boolean or number or string        | no       | What customers without the feature get. Default: false, 0, or the first enum value.                                                                                                                      |
| `perUnit`         | boolean                            | no       | Limit features only: multiply the value a product grants by the quantity bought or subscribed (e.g. 5 projects per seat). Default false.                                                                 |
| `entity_id`       | ID (`ent_…`)                       | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`)                       | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string                             | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean                            | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is malformed, the combine rule doesn't fit the type, the default isn't a valid value, or `perUnit` is set on a feature that isn't a limit.
- [`feature_key_taken`](https://ownrecord.io/docs/reference/errors.md#feature_key_taken): The brand already has a feature with this key in this mode.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's catalog.

### Example

**A seat limit, per unit**

Arguments:

```json
{
  "key": "seats.max",
  "name": "Seats",
  "type": "limit",
  "defaultValue": 1,
  "perUnit": true,
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "feat_01m4tx5nj8emwbyf7y9vcf47y6",
  "object": "feature",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "key": "seats.max",
  "name": "Seats",
  "type": "limit",
  "enumValues": null,
  "combine": "max",
  "defaultValue": 1,
  "perUnit": true,
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

## `features_list`

**List features.** Operation [`features.list`](https://ownrecord.io/docs/reference/features-list.md) · Brand level · permission `catalog:read` · risk tier `read` · since M4

Lists the brand's features in the request's mode, newest first. Filter with `key` to find one feature.

Paginate with `limit` (1–100, default 10) and `starting_after` (the `next_cursor` of the previous page).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type          | Required | Description                                                                                                                              |
| ---------------- | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `key`            | string        | no       | Only the feature with this key. (at most 100 characters)                                                                                 |
| `limit`          | integer       | no       | (1–100)                                                                                                                                  |
| `starting_after` | ID (`feat_…`) | no       | –                                                                                                                                        |
| `entity_id`      | ID (`ent_…`)  | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                 |
| `brand_id`       | ID (`brd_…`)  | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is malformed.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this brand's catalog.

### Example

**The brand's features**

Arguments:

```json
{
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "feat_01m4tx5nj8emwbyf7y9vcf47y6",
      "object": "feature",
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "livemode": false,
      "key": "seats.max",
      "name": "Seats",
      "type": "limit",
      "enumValues": null,
      "combine": "max",
      "defaultValue": 1,
      "perUnit": true,
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## `features_update`

**Update a feature.** Operation [`features.update`](https://ownrecord.io/docs/reference/features-update.md) · Brand level · permission `catalog:write` · risk tier `write` · since M4

Changes a feature's name, combine rule, default value, `perUnit` (limits) or (for enum features) its values.
Send only the fields to change. The key and type never change, and enum values can be added but not removed,
so values products already grant stay valid. Customers' entitlements are recomputed after a change of the
combine rule, default value or `perUnit` (`customer.entitlements.updated` for each customer whose values move).

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type                               | Required | Description                                                                                                                                                                                              |
| ----------------- | ---------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`feat_…`)                      | yes      | –                                                                                                                                                                                                        |
| `name`            | string                             | no       | (1–120 characters)                                                                                                                                                                                       |
| `combine`         | one of `max`, `sum`, `or`, `first` | no       | How values from several products combine: `or` or `first` for booleans, `max`, `sum` or `first` for limits, `first` for enums.                                                                           |
| `defaultValue`    | boolean or number or string        | no       | true/false for a boolean feature, a whole number for a limit, one of the values for an enum.                                                                                                             |
| `enumValues`      | array of string                    | no       | Enum features: the full list of values; existing values must stay (new ones can be added). (1–50 items)                                                                                                  |
| `perUnit`         | boolean                            | no       | Limit features only: multiply granted values by the quantity. Customers' entitlements follow.                                                                                                            |
| `entity_id`       | ID (`ent_…`)                       | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`)                       | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string                             | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean                            | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): No field to change was sent, or the new definition breaks a rule (e.g. `perUnit` on a boolean).
- [`feature_change_not_allowed`](https://ownrecord.io/docs/reference/errors.md#feature_change_not_allowed): The change removes enum values.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No feature with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The feature changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's catalog.

### Example

**Rename a feature**

Arguments:

```json
{
  "id": "feat_01m4tx5nj8emwbyf7y9vcf47y6",
  "name": "Team seats",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "feat_01m4tx5nj8emwbyf7y9vcf47y6",
  "object": "feature",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "key": "seats.max",
  "name": "Team seats",
  "type": "limit",
  "enumValues": null,
  "combine": "max",
  "defaultValue": 1,
  "perUnit": true,
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```
