Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 66 additions & 8 deletions fern/definition/api-keys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>
Expand All @@ -35,12 +41,6 @@ types:
inbox_delete:
type: optional<boolean>
docs: Delete inboxes.
thread_read:
type: optional<boolean>
docs: Read threads.
thread_delete:
type: optional<boolean>
docs: Delete threads.
message_read:
type: optional<boolean>
docs: Read messages.
Expand All @@ -50,6 +50,9 @@ types:
message_update:
type: optional<boolean>
docs: Update message labels.
message_delete:
type: optional<boolean>
docs: Delete messages.
label_spam_read:
type: optional<boolean>
docs: Access messages labeled spam.
Expand Down Expand Up @@ -116,6 +119,9 @@ types:
api_key_create:
type: optional<boolean>
docs: Create API keys.
api_key_update:
type: optional<boolean>
docs: Update API keys.
api_key_delete:
type: optional<boolean>
docs: Delete API keys.
Expand Down Expand Up @@ -174,7 +180,40 @@ types:
CreateApiKeyRequest:
properties:
name: Name
permissions: optional<ApiKeyPermissions>
permissions:
type: optional<ApiKeyPermissions>
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<Name>
permissions:
type: optional<ApiKeyPermissions>
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
Expand Down Expand Up @@ -202,6 +241,20 @@ service:
response: CreateApiKeyResponse
errors:
- global.ValidationError
- ForbiddenError
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: ForbiddenError is missing from the create and update endpoints in the pod-scoped and inbox-scoped API key files (fern/definition/pods/api-keys.yml, fern/definition/inboxes/api-keys.yml). Both scopes share the same request types whose docs describe 403 responses for permission escalation, so their error lists should be consistent with the org scope.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At fern/definition/api-keys.yml, line 244:

<comment>`ForbiddenError` is missing from the `create` and `update` endpoints in the pod-scoped and inbox-scoped API key files (`fern/definition/pods/api-keys.yml`, `fern/definition/inboxes/api-keys.yml`). Both scopes share the same request types whose docs describe 403 responses for permission escalation, so their error lists should be consistent with the org scope.</comment>

<file context>
@@ -210,6 +241,7 @@ service:
       response: CreateApiKeyResponse
       errors:
         - global.ValidationError
+        - ForbiddenError
 
     update:
</file context>
Fix with Cubic


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
- ForbiddenError

delete:
method: DELETE
Expand All @@ -211,3 +264,8 @@ service:
api_key_id: ApiKeyId
errors:
- global.NotFoundError

errors:
ForbiddenError:
status-code: 403
type: global.ErrorResponse
12 changes: 12 additions & 0 deletions fern/definition/inboxes/api-keys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
12 changes: 12 additions & 0 deletions fern/definition/pods/api-keys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down