
# Update a permission

`PATCH /v1/permissions/{permission_id}` · permission `apis:write`

Updates a permission with a JSON merge patch (RFC 7396, `application/merge-patch+json`): the fields you send change, the others stay. Permissions have no version, so there is no `If-Match`.

## Request

| Parameter | In | Description |
|---|---|---|
| `permission_id` | path | Permission ID (`perm_…`). |

Body: any of the fields below (all optional, at least one).

| Field | Type | Required | Description |
|---|---|---|---|
| `value` | string | no | Accepted only when equal to the current value; it never changes. |
| `display_name` | string \| null | no | Short name shown on the consent screen instead of the scope value. At most 128 characters. |
| `description` | string \| null | no | Detail line shown under the name on the consent screen. At most 1024 characters. |
| `consent_text_i18n` | object \| null | no | Language tag → consent screen text, for example `{"en": "Read your orders"}`. At most 30 languages, each text 1-1024 characters, 16 KiB in total. |
| `sensitivity` | string | no | `normal` (default) or `sensitive`. |

`display_name`, `description` and `consent_text_i18n` are cleared with `null`; `sensitivity` cannot be cleared. `consent_text_i18n` is replaced as a whole: languages you leave out are removed, so send every language you want to keep.

### Consent screen texts

The hosted consent screen shows each requested scope as follows: the `consent_text_i18n` entry for the page language (exact tag, otherwise its primary subtag), otherwise `display_name`, otherwise the scope `value`; `description` is shown as a detail line. All three are plain text. Control characters (including line breaks and tabs), line and paragraph separators and bidirectional embedding, override and isolate characters (U+202A-U+202E, U+2066-U+2069) are rejected with `422`. Leading and trailing whitespace is trimmed, and a text that is empty after trimming is stored as `null`. HTML is not interpreted: the consent screen escapes it.

## Response

`200 OK`

```json
{
  "id": "perm_01h455vb4pex5vsknk084sn02s",
  "api_id": "api_01h455vb4pex5vsknk084sn02q",
  "value": "read:orders",
  "display_name": "Read orders",
  "description": "See your past orders and their status",
  "consent_text_i18n": {
    "en": "Read your orders",
    "tr": "Siparişlerinizi okuyun"
  },
  "sensitivity": "normal"
}
```

## Errors

| HTTP | code | When |
|---|---|---|
| 400 | `unknown_field` | The patch contains `api_id` or an unknown field. |
| 404 | `not_found` | There is no such permission in this environment. |
| 409 | `conflict` | The permission belongs to the system API. |
| 415 | `unsupported_media_type` | The content type is not `application/merge-patch+json`. |
| 422 | `validation_failed` | The patch is empty, a value is invalid or not plain text, `sensitivity` is `null`, or `value` differs (`immutable`). |

## Example

```bash
curl -X PATCH https://api.eu.keenauth.com/v1/permissions/perm_01h455vb4pex5vsknk084sn02s \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{"description":"See your past orders and their status","consent_text_i18n":{"en":"Read your orders","tr":"Siparişlerinizi okuyun"}}'
```
