# Update a feature

> The features.update operation: PATCH /api/v1/features/{id} in the REST API and the features_update tool in the MCP server.

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).

## At a glance

- **REST:** `PATCH /api/v1/features/{id}`
- **MCP tool:** `features_update`
- **Operation ID:** `features.update`
- **Scope:** Brand
- **Permission:** `catalog:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` optional
- **Modes:** Test and live
- **Authentication:** API key with the `catalog:write` scope
- **Since:** M4

## Request

### Path parameters

| Field | Type        | Description |
| ----- | ----------- | ----------- |
| `id`  | `feat_…` ID |             |

### Body

A JSON object (`Content-Type: application/json`):

| Field          | Type                               | Required | Description                                                                                                                    |
| -------------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `name`         | string                             | No       |                                                                                                                                |
| `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).                                     |
| `perUnit`      | boolean                            | No       | Limit features only: multiply granted values by the quantity. Customers' entitlements follow.                                  |

## Response

`200 OK` with a JSON object:

| Field          | Type                               | Description                                                                                   |
| -------------- | ---------------------------------- | --------------------------------------------------------------------------------------------- |
| `id`           | `feat_…` ID                        |                                                                                               |
| `object`       | `"feature"`                        |                                                                                               |
| `brandId`      | `brd_…` ID                         |                                                                                               |
| `livemode`     | boolean                            |                                                                                               |
| `key`          | string                             |                                                                                               |
| `name`         | string                             |                                                                                               |
| `type`         | one of `boolean`, `limit`, `enum`  |                                                                                               |
| `enumValues`   | array of string or null            |                                                                                               |
| `combine`      | one of `max`, `sum`, `or`, `first` |                                                                                               |
| `defaultValue` | boolean or number or string        |                                                                                               |
| `perUnit`      | boolean                            | Limit features only: values are multiplied by the item's or purchase's quantity (spec §36.1). |
| `createdAt`    | date-time                          |                                                                                               |
| `updatedAt`    | date-time                          |                                                                                               |

## Examples

### Rename a feature

```http
PATCH /api/v1/features/feat_01m4tx5nj8emwbyf7y9vcf47y6
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "name": "Team seats"
}
```

Response (`200`):

```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"
}
```

## Errors

| Code                                                                                                     | HTTP status | When                                                                                            | What to do                                                                                           |
| -------------------------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                   | 400         | No field to change was sent, or the new definition breaks a rule (e.g. `perUnit` on a boolean). | Fix the fields listed in `details.issues` and send the request again.                                |
| [`feature_change_not_allowed`](https://ownrecord.io/docs/reference/errors.md#feature_change_not_allowed) | 409         | The change removes enum values.                                                                 | Create a new feature with the new definition, and grant it from your products instead.               |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)                 | 404         | No feature with this ID is visible to the request.                                              | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)                     | 409         | The feature changed at the same time; retry.                                                    | Fetch the object again and retry the change.                                                         |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                   | 403         | You can't change this brand's catalog.                                                          | Ask an organization owner or admin for a role or scope that allows this action.                      |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
