From 97ee9cc9a0aea7c7b3524c680c2b58e828725941 Mon Sep 17 00:00:00 2001 From: Joseph Feleke <53655514+josephfeleke@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:18:12 -0500 Subject: [PATCH 1/2] Add Update API Key endpoint documentation Add PATCH endpoint for updating API key name and permissions across org, pod, and inbox scopes. Add api_key_update permission to ApiKeyPermissions. --- fern/definition/api-keys.yml | 20 ++++++++++++++++++++ fern/definition/inboxes/api-keys.yml | 12 ++++++++++++ fern/definition/pods/api-keys.yml | 12 ++++++++++++ 3 files changed, 44 insertions(+) diff --git a/fern/definition/api-keys.yml b/fern/definition/api-keys.yml index 4ce2c723..b0b87bd8 100644 --- a/fern/definition/api-keys.yml +++ b/fern/definition/api-keys.yml @@ -116,6 +116,9 @@ types: api_key_create: type: optional docs: Create API keys. + api_key_update: + type: optional + docs: Update API keys. api_key_delete: type: optional docs: Delete API keys. @@ -176,6 +179,11 @@ types: name: Name permissions: optional + UpdateApiKeyRequest: + properties: + name: optional + permissions: optional + service: url: Http base-path: /api-keys @@ -203,6 +211,18 @@ service: errors: - global.ValidationError + update: + method: PATCH + path: /{api_key_id} + display-name: Update API Key + path-parameters: + api_key_id: ApiKeyId + request: UpdateApiKeyRequest + response: ApiKey + errors: + - global.NotFoundError + - global.ValidationError + delete: method: DELETE path: /{api_key_id} diff --git a/fern/definition/inboxes/api-keys.yml b/fern/definition/inboxes/api-keys.yml index 150d2162..0601a09a 100644 --- a/fern/definition/inboxes/api-keys.yml +++ b/fern/definition/inboxes/api-keys.yml @@ -36,6 +36,18 @@ service: - global.NotFoundError - global.ValidationError + update: + method: PATCH + path: /{api_key_id} + display-name: Update API Key + path-parameters: + api_key_id: api-keys.ApiKeyId + request: api-keys.UpdateApiKeyRequest + response: api-keys.ApiKey + errors: + - global.NotFoundError + - global.ValidationError + delete: method: DELETE path: /{api_key_id} diff --git a/fern/definition/pods/api-keys.yml b/fern/definition/pods/api-keys.yml index f082980a..73baa729 100644 --- a/fern/definition/pods/api-keys.yml +++ b/fern/definition/pods/api-keys.yml @@ -36,6 +36,18 @@ service: - global.NotFoundError - global.ValidationError + update: + method: PATCH + path: /{api_key_id} + display-name: Update API Key + path-parameters: + api_key_id: api-keys.ApiKeyId + request: api-keys.UpdateApiKeyRequest + response: api-keys.ApiKey + errors: + - global.NotFoundError + - global.ValidationError + delete: method: DELETE path: /{api_key_id} From 3d5060ba2e32f9c257c548cd9021dfee0827d618 Mon Sep 17 00:00:00 2001 From: Joseph Feleke <53655514+josephfeleke@users.noreply.github.com> Date: Fri, 24 Apr 2026 19:24:00 -0700 Subject: [PATCH 2/2] api-keys: sync docs with merged update endpoint - Add `message_delete`; remove non-existent `thread_read` and `thread_delete` (replaced by message_* permissions in the API months ago, docs lagged). - Document permissions semantics on Create and Update endpoints: - Omitted: inherit (Create) / no-op (Update) - null: make unrestricted (gated to unrestricted callers; 403 otherwise) - Populated: merge with stored, per-field escalation rejected - Add `ForbiddenError` (403) on Create and Update. - Note "at least one of name or permissions" requirement on Update. --- fern/definition/api-keys.yml | 56 ++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/fern/definition/api-keys.yml b/fern/definition/api-keys.yml index b0b87bd8..840b125b 100644 --- a/fern/definition/api-keys.yml +++ b/fern/definition/api-keys.yml @@ -21,7 +21,13 @@ types: docs: Time at which api key was created. ApiKeyPermissions: - docs: Granular permissions for the API key. When ommitted all permissions are granted. Otherwise, only permissions set to true are granted. + docs: | + Granular permissions for an API key. Each field is an independent + boolean grant — only fields set to `true` are granted. A caller cannot + grant a permission they do not themselves hold; per-field escalation is + rejected with 403. See the `permissions` field on `Create API Key` and + `Update API Key` for how `null`, omitted, and populated values are + interpreted on each endpoint. properties: inbox_read: type: optional @@ -35,12 +41,6 @@ types: inbox_delete: type: optional docs: Delete inboxes. - thread_read: - type: optional - docs: Read threads. - thread_delete: - type: optional - docs: Delete threads. message_read: type: optional docs: Read messages. @@ -50,6 +50,9 @@ types: message_update: type: optional docs: Update message labels. + message_delete: + type: optional + docs: Delete messages. label_spam_read: type: optional docs: Access messages labeled spam. @@ -177,12 +180,40 @@ types: CreateApiKeyRequest: properties: name: Name - permissions: optional + permissions: + type: optional + docs: | + Permissions for the new key. Behavior depends on the value: + - Omitted: the new key inherits the creator's permissions + (unrestricted creators get an unrestricted child; restricted + creators get a child with their own granular permissions). + - `null`: makes the new key unrestricted. Only allowed when the + creator is themselves unrestricted; restricted creators sending + `null` get a 403. + - Populated object: grants the listed `true` fields, intersected + with the creator's own permissions. Per-field escalation + (granting a permission the creator doesn't hold) is rejected + with 403. UpdateApiKeyRequest: + docs: At least one of `name` or `permissions` must be provided. properties: name: optional - permissions: optional + permissions: + type: optional + docs: | + Permissions to apply. Behavior depends on the value: + - Omitted: the key's permissions are unchanged. + - `null`: clears all restrictions and makes the key unrestricted. + Only allowed when the caller is themselves unrestricted; + restricted callers sending `null` get a 403. The same gate + applies when the target key is currently unrestricted and the + caller tries to make it restricted — only unrestricted callers + can flip a key between restricted and unrestricted. + - Populated object: merged with the stored permissions — fields + not mentioned are preserved, mentioned fields overwrite. + Per-field escalation (granting a permission the caller doesn't + hold) is rejected with 403. service: url: Http @@ -210,6 +241,7 @@ service: response: CreateApiKeyResponse errors: - global.ValidationError + - ForbiddenError update: method: PATCH @@ -222,6 +254,7 @@ service: errors: - global.NotFoundError - global.ValidationError + - ForbiddenError delete: method: DELETE @@ -231,3 +264,8 @@ service: api_key_id: ApiKeyId errors: - global.NotFoundError + +errors: + ForbiddenError: + status-code: 403 + type: global.ErrorResponse