Skip to content

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Oct 28, 2025

What does this PR do?

This cleans up the openapi spec for the credits, so they can be used by both keys and credits

Doesn't pass any tests either but its fine as we are using this as base for the next pr

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

Please double check if the schema makes sense in regards to nullability.

The api will throw a build error since the types got changed which is fixed in the next big pr

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@changeset-bot
Copy link

changeset-bot bot commented Oct 28, 2025

⚠️ No Changeset found

Latest commit: a086941

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Oct 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
dashboard Ready Ready Preview Comment Oct 30, 2025 3:13pm
engineering Ready Ready Preview Comment Oct 30, 2025 3:13pm

Copy link
Member Author

Flo4604 commented Oct 28, 2025

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

📝 Walkthrough

Walkthrough

This change refactors credit-related schemas and types across identities and keys in the OpenAPI specification and generated Go code. It introduces consolidated types (CreditInterval, CreditOperation, Credits, UpdateCredits, VerifyKeyIdentity) replacing key-specific variants, and integrates credits handling at both identity and key levels with new endpoints and modified request/response structures.

Changes

Cohort / File(s) Summary
Go Generated Types
go/apps/api/openapi/gen.go
Added new types: CreditInterval, CreditOperation, Credits, CreditsRefill, UpdateCredits, UpdateCreditsRefill, VerifyKeyIdentity, V2IdentitiesUpdateCreditsRequestBody, V2IdentitiesUpdateCreditsResponseBody. Modified field references to use Credits instead of KeyCreditsData; changed operation type to CreditOperation. Added Credits field to Identity struct. Removed V2KeysUpdateCreditsRequestBodyOperation type. Added JSON request/response body aliases.
OpenAPI Spec Files
go/apps/api/openapi/openapi-generated.yaml, go/apps/api/openapi/openapi-split.yaml
Updated schemas to reference new consolidated Credits types. Added new /v2/identities.updateCredits endpoint. Updated Identity, Key, and Verify request/response models to use new credit types and VerifyKeyIdentity.
Overlay Configuration
go/apps/api/openapi/overlay.yaml
Renamed KeyCreditsData → Credits, UpdateKeyCreditsData → UpdateCredits, UpdateKeyCreditsRefill → UpdateCreditsRefill. Updated property references and added nullable constraints to UpdateCreditsRefill.
Common Credit Schemas
go/apps/api/openapi/spec/common/CreditInterval.yaml, go/apps/api/openapi/spec/common/CreditOperation.yaml, go/apps/api/openapi/spec/common/Credits.yaml, go/apps/api/openapi/spec/common/CreditsRefill.yaml, go/apps/api/openapi/spec/common/UpdateCredits.yaml, go/apps/api/openapi/spec/common/UpdateCreditsRefill.yaml
Added new CreditInterval and CreditOperation enum schemas. Updated Credits and CreditsRefill to reference external CreditInterval schema. Introduced new UpdateCredits and UpdateCreditsRefill schemas supporting null semantics for disabling credits/refills.
Identity Schema & Endpoints
go/apps/api/openapi/spec/common/Identity.yaml, go/apps/api/openapi/spec/paths/v2/identities/createIdentity/..., go/apps/api/openapi/spec/paths/v2/identities/updateIdentity/..., go/apps/api/openapi/spec/paths/v2/identities/updateCredits/...
Added credits property to Identity schema. Extended V2IdentitiesCreateIdentityRequestBody and V2IdentitiesUpdateIdentityRequestBody with credits fields. Added new V2IdentitiesUpdateCreditsRequestBody/ResponseBody schemas and POST /v2/identities.updateCredits endpoint supporting set/increment/decrement operations.
Key Schemas & Endpoints
go/apps/api/openapi/spec/common/KeyResponseData.yaml, go/apps/api/openapi/spec/paths/v2/keys/createKey/..., go/apps/api/openapi/spec/paths/v2/keys/updateKey/..., go/apps/api/openapi/spec/paths/v2/keys/updateCredits/..., go/apps/api/openapi/spec/paths/v2/keys/verifyKey/...
Updated key credits references from KeyCreditsData to Credits. Changed V2KeysUpdateKeyRequestBody.credits reference from UpdateKeyCreditsData to UpdateCredits. Removed UpdateKeyCreditsData.yaml. Updated V2KeysUpdateCreditsRequestBody.operation to reference CreditOperation. Renamed operationId/x-speakeasy-name-override from updateCredits → updateKeyCredits. Updated V2KeysVerifyKeyResponseData.identity from Identity → VerifyKeyIdentity. Added new VerifyKeyIdentity schema with id, externalId, meta, and ratelimits fields.

Sequence Diagram

sequenceDiagram
    participant Client
    participant API
    participant Handler
    
    rect rgb(200, 220, 255)
    Note over Client,Handler: Identity-Level Credits Update (new)
    Client->>API: POST /v2/identities.updateCredits<br/>(identity, operation, value)
    API->>Handler: updateIdentityCredits<br/>(CreditOperation: set|increment|decrement)
    Handler->>Handler: Apply operation to identity credits<br/>(value or null for unlimited)
    Handler-->>API: Credits updated<br/>(remaining, refill)
    API-->>Client: 200 OK<br/>V2IdentitiesUpdateCreditsResponseBody
    end

    rect rgb(220, 200, 255)
    Note over Client,Handler: Key-Level Credits Update (refactored)
    Client->>API: PATCH /v2/keys/{keyId}/updateCredits<br/>(operation, value)
    API->>Handler: updateKeyCredits<br/>(CreditOperation: set|increment|decrement)
    Handler->>Handler: Apply operation to key credits<br/>(or identity-level credits if prioritized)
    Handler-->>API: Credits updated<br/>(Credits type)
    API-->>Client: 200 OK<br/>V2KeysUpdateCreditsResponseBody
    end

    rect rgb(200, 255, 220)
    Note over Client,Handler: Key Verification (identity shape refined)
    Client->>API: GET /v2/keys/{key}/verify
    API->>Handler: verifyKey
    Handler->>Handler: Validate key & retrieve identity
    Handler-->>API: VerifyKeyIdentity<br/>(id, externalId, meta, ratelimits)
    API-->>Client: 200 OK<br/>V2KeysVerifyKeyResponseData<br/>(includes VerifyKeyIdentity)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • Cross-references consistency: Verify all $ref updates from KeyCreditsData → Credits and UpdateKeyCreditsData → UpdateCredits are complete and correct across all endpoint definitions
  • Nullability semantics: Confirm null behavior for unlimited credits and disabled refills is correctly expressed in UpdateCredits and UpdateCreditsRefill schemas
  • Identity vs. key credit precedence: Validate that identity-level credits properly override key-level credits in implementation logic and documentation
  • New VerifyKeyIdentity type: Ensure all fields (id, externalId, meta, ratelimits) align with Identity schema expectations and don't break downstream consumers
  • CreditOperation enum usage: Confirm set/increment/decrement operations are correctly applied across both identity and key endpoints
  • Endpoint operation naming: Verify updateKeyCredits (renamed from updateCredits) doesn't conflict with identity's updateIdentityCredits operationId

Possibly related PRs

Suggested labels

Core Team

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description addresses the main template sections but has significant gaps that indicate incomplete work. The "What does this PR do?" section is brief and lacks the issue reference required by the template. The "Type of change" section correctly identifies this as a Chore. However, the "How should this be tested?" section provides only vague guidance ("Please double check if the schema makes sense in regards to nullability") rather than specific, reproducible test steps. Most critically, all required checklist items are unchecked, suggesting the author has not completed prerequisite tasks like running build/format commands, self-review, or merging latest changes from main. While the author acknowledges the PR intentionally doesn't pass tests because it's a base for the next PR in a stack, the incomplete checklist and vague testing instructions do not meet the description completeness standards. The description should be updated to include: (1) an issue reference (Fixes #XXXX) linking this work to a tracked issue; (2) specific, reproducible testing instructions beyond the vague "double check" request—for example, listing concrete validation steps for schema nullability and explaining how reviewers can verify the changes; and (3) checking off the required checklist items that have been completed, or explicitly explaining which items are deferred to the next PR in the stack. At minimum, clarify in "How should this be tested?" what specific schema elements should be validated and provide a clear path for testing, even if full CI/CD validation is deferred.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "feat: add identity-based credits OpenAPI specifications" is directly related to the changeset, which includes introducing identity-level credits support, new credit-related schemas (CreditInterval, CreditOperation, Credits, UpdateCredits), and updating OpenAPI specifications across multiple files. While the title emphasizes the identity-based aspect, the primary objective described in the PR is consolidating and refactoring the credits model so it can be used by both keys and identities. The title captures a significant and real part of these changes, though it doesn't fully represent the core refactoring goal of making credits generic and reusable across domains.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/identity-credits-openapi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Flo4604 Flo4604 force-pushed the feat/identity-credits-openapi branch from ce27d21 to 19aadaf Compare October 28, 2025 21:09
@Flo4604 Flo4604 force-pushed the feat/identity-credits-openapi branch from 19aadaf to 03872fb Compare October 28, 2025 21:45
@Flo4604 Flo4604 force-pushed the feat/identity-credits-api-v1 branch from c300f90 to aa1950a Compare October 28, 2025 22:40
@Flo4604 Flo4604 force-pushed the feat/identity-credits-openapi branch from 03872fb to 6816e5e Compare October 28, 2025 22:40
@Flo4604 Flo4604 force-pushed the feat/identity-credits-api-v1 branch from aa1950a to baf6928 Compare October 29, 2025 09:59
@Flo4604 Flo4604 force-pushed the feat/identity-credits-openapi branch from 6816e5e to c199c71 Compare October 29, 2025 09:59
@Flo4604 Flo4604 force-pushed the feat/identity-credits-openapi branch from c199c71 to 7397a54 Compare October 29, 2025 10:21
@Flo4604 Flo4604 force-pushed the feat/identity-credits-api-v1 branch from 3369e55 to 8d4a3a4 Compare October 29, 2025 18:01
- Add Credits and CreditsRefill schema definitions
- Add CreditOperation and CreditInterval enums
- Add updateCredits endpoints for keys and identities
- Update key and identity schemas to include credits field
- Add UpdateCredits and UpdateCreditsRefill request/response types
- Update generated OpenAPI specs
@@ -0,0 +1,20 @@
type: object
Copy link
Member Author

Choose a reason for hiding this comment

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

Personally im not sure about this change, but I didn't want to pollute our respond withs a full blown our credits object that includes the refillDates as I dont think it would be usefull in a verification context.

@Flo4604 Flo4604 marked this pull request as ready for review October 30, 2025 15:16
@Flo4604
Copy link
Member Author

Flo4604 commented Oct 30, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
go/apps/api/openapi/openapi-generated.yaml (7)

3799-3805: Example field name doesn’t match schema (identity vs externalId).

V2IdentitiesGetIdentityRequestBody requires property "identity" (accepts externalId or identityId), but the example uses "externalId". Align the example with the schema.

-                                    externalId: user_123
+                                    identity: user_123

3917-3941: List identities example shape disagrees with the schema.

Schema says data is an array (V2IdentitiesListIdentitiesResponseData) and pagination lives in "pagination". Example shows data as an object with cursor/identities/total.

-                                        data:
-                                            cursor: cursor_eyJsYXN0SWQiOiJpZF8wMlpZUjNROU5QOEpNNFg4SFdTS1BXNDNKRiJ9
-                                            identities:
-                                                - externalId: user_123
-                                                  id: id_01H9TQP8NP8JN3X8HWSKPW43JE
-                                                  meta:
-                                                    name: Alice Smith
-                                                    plan: premium
-                                                  ratelimits:
-                                                    - duration: 60000
-                                                      limit: 1000
-                                                      name: requests
-                                                - externalId: user_456
-                                                  id: id_02ZYR3Q9NP8JM4X8HWSKPW43JF
-                                                  meta:
-                                                    name: Bob Johnson
-                                                    plan: basic
-                                                  ratelimits:
-                                                    - duration: 60000
-                                                      limit: 500
-                                                      name: requests
-                                            total: 247
+                                        data:
+                                            - externalId: user_123
+                                              id: id_01H9TQP8NP8JN3X8HWSKPW43JE
+                                              meta:
+                                                name: Alice Smith
+                                                plan: premium
+                                              ratelimits:
+                                                - duration: 60000
+                                                  limit: 1000
+                                                  name: requests
+                                            - externalId: user_456
+                                              id: id_02ZYR3Q9NP8JM4X8HWSKPW43JF
+                                              meta:
+                                                name: Bob Johnson
+                                                plan: basic
+                                              ratelimits:
+                                                - duration: 60000
+                                                  limit: 500
+                                                  name: requests
+                                        pagination:
+                                            cursor: cursor_eyJsYXN0SWQiOiJpZF8wMlpZUjNROU5QOEpNNFg4SFdTS1BXNDNKRiJ9
+                                            hasMore: true

3986-3993: x-speakeasy pagination pointer likely incorrect.

nextCursor is set to $.data.cursor, but per schema pagination.cursor is at top-level "pagination". Update pointer to avoid SDK pagination bugs.

-                outputs:
-                    nextCursor: $.data.cursor
+                outputs:
+                    nextCursor: $.pagination.cursor

1134-1142: int64 maximum exceeds valid range.

Several int64 fields use maximum: 9223372036854776000 (> int64 max 9223372036854775807). This breaks validation and some generators.

  • Replace all occurrences of 9223372036854776000 with 9223372036854775807 for int64-typed fields (e.g., V2KeysUpdateCreditsRequestBody.value, KeyResponseData.createdAt/updatedAt/expires, etc.).

Also applies to: 2197-2216, 2209-2216


2828-2835: verifyKey response credits type vs description mismatch + source ambiguity.

Type is integer (int32) and not nullable, but description says “If null or not present”. Also, with identity credits taking priority, this value should reflect the effective source.

-                credits:
-                    type: integer
-                    format: int32
-                    description: |
-                        The number of requests/credits remaining for this key. If null
-                        or not present, the key has unlimited usage. This value decreases with
-                        each verification (based on the 'cost' parameter) unless explicit credit
-                        refills are configured.
+                credits:
+                    type:
+                      - integer
+                      - "null"
+                    format: int64
+                    description: |
+                        Effective remaining credits after applying identity-over-key precedence.
+                        Null means unlimited. Decrements by the verification 'cost' when > 0.
+                creditsSource:
+                    type: string
+                    enum: [identity, key]
+                    description: Which pool determined the effective remaining credits.

732-745: Permission slug pattern inconsistent with other endpoints.

Here permissions pattern is "^[a-zA-Z][a-zA-Z0-9._-]$", while add/remove/set endpoints allow colons, dashes, dots, underscores, and wildcard "". Unify to prevent clients from failing on createKey.

-                        pattern: "^[a-zA-Z][a-zA-Z0-9._-]*$"
+                        pattern: "^[a-zA-Z0-9_:\\-\\.\\*]+$"

1619-1627: Copy/paste description: getRole request mentions delete.

V2PermissionsGetRoleRequestBody description should describe retrieval, not deletion.

-                        Unique identifier of the role to permanently delete from your workspace.
+                        Unique identifier of the role to retrieve from your workspace.
go/apps/api/openapi/gen.go (1)

1426-1434: Fix overflow risk in verify response credits field.
Line 1426 still exposes Credits as *int32, yet every other credits payload in this refactor moved to int64. The first time an identity or key has more than 2,147,483,647 credits, the verify-key response will truncate or overflow. Please upgrade this field to *int64 (or swap in the new Credits model) so the contract matches the wider range introduced here.

Apply this diff:

-	// Credits The number of requests/credits remaining for this key. If null
-	// or not present, the key has unlimited usage. This value decreases with
-	// each verification (based on the 'cost' parameter) unless explicit credit
-	// refills are configured.
-	Credits *int32 `json:"credits,omitempty"`
+	// Credits The number of requests/credits remaining for this key. If null
+	// or not present, the key has unlimited usage. This value decreases with
+	// each verification (based on the 'cost' parameter) unless explicit credit
+	// refills are configured.
+	Credits *int64 `json:"credits,omitempty"`
🧹 Nitpick comments (3)
go/apps/api/openapi/spec/paths/v2/keys/createKey/V2KeysCreateKeyRequestBody.yaml (1)

120-126: Description clarity: clarify null handling during key creation.

The description states "setting null is not allowed during creation," but the Credits schema permits remaining: null for unlimited usage. While this may be enforced at the handler level, consider rewording to clarify that only the entire credits field can be omitted (for unlimited), or that during key creation remaining cannot be null—whereas it may be nullable in updates. This aligns with your note in the PR about schema nullability requiring verification.

go/apps/api/openapi/spec/paths/v2/identities/updateIdentity/V2IdentitiesUpdateIdentityRequestBody.yaml (1)

36-37: LGTM – Clean optional credits field.

The credits field correctly references UpdateCredits.yaml and remains optional so omitting it preserves existing credits. This follows the same pattern as the meta and ratelimits fields above it.

Minor suggestion: Consider adding an inline description for the credits field to match the pattern of meta and ratelimits (which have field-level documentation). The current design relies entirely on the UpdateCredits.yaml schema's description.

go/apps/api/openapi/openapi-generated.yaml (1)

2727-2737: Clarify zero-cost verify semantics.

Add an explicit sentence that cost: 0 does not decrement remaining credits but still returns the true remaining amount, matching limiter behavior. Based on learnings.

                     description: |
                         Sets how many credits to deduct for this verification request.
                         Use 0 for read-only operations or free tier access, higher values for premium features.
-                        Credits are deducted after all security checks pass.
+                        Credits are deducted after all security checks pass.
+                        When cost is 0, remaining credits are not decremented but are still reported accurately.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 775d35a and a086941.

📒 Files selected for processing (26)
  • go/apps/api/openapi/gen.go (14 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (20 hunks)
  • go/apps/api/openapi/openapi-split.yaml (1 hunks)
  • go/apps/api/openapi/overlay.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/CreditInterval.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/CreditOperation.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/Credits.yaml (2 hunks)
  • go/apps/api/openapi/spec/common/CreditsRefill.yaml (2 hunks)
  • go/apps/api/openapi/spec/common/Identity.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/KeyResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/UpdateCredits.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/UpdateCreditsRefill.yaml (2 hunks)
  • go/apps/api/openapi/spec/paths/v2/identities/createIdentity/V2IdentitiesCreateIdentityRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/identities/getIdentity/index.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/identities/updateCredits/V2IdentitiesUpdateCreditsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/identities/updateCredits/V2IdentitiesUpdateCreditsResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/identities/updateCredits/index.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/identities/updateIdentity/V2IdentitiesUpdateIdentityRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/createKey/V2KeysCreateKeyRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/V2KeysUpdateCreditsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/V2KeysUpdateCreditsResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/index.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/UpdateKeyCreditsData.yaml (0 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/VerifyKeyIdentity.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/UpdateKeyCreditsData.yaml
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: Flo4604
PR: unkeyed/unkey#4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.710Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.
Learnt from: Flo4604
PR: unkeyed/unkey#3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
📚 Learning: 2025-10-30T15:10:52.710Z
Learnt from: Flo4604
PR: unkeyed/unkey#4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.710Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.

Applied to files:

  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/V2KeysUpdateCreditsRequestBody.yaml
  • go/apps/api/openapi/spec/common/KeyResponseData.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/index.yaml
  • go/apps/api/openapi/spec/paths/v2/identities/updateIdentity/V2IdentitiesUpdateIdentityRequestBody.yaml
  • go/apps/api/openapi/spec/common/UpdateCredits.yaml
  • go/apps/api/openapi/spec/common/Identity.yaml
  • go/apps/api/openapi/spec/paths/v2/identities/updateCredits/index.yaml
  • go/apps/api/openapi/spec/common/Credits.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml
  • go/apps/api/openapi/spec/paths/v2/identities/createIdentity/V2IdentitiesCreateIdentityRequestBody.yaml
  • go/apps/api/openapi/spec/paths/v2/identities/updateCredits/V2IdentitiesUpdateCreditsRequestBody.yaml
  • go/apps/api/openapi/overlay.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/createKey/V2KeysCreateKeyRequestBody.yaml
  • go/apps/api/openapi/gen.go
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/VerifyKeyIdentity.yaml
  • go/apps/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-03T05:58:10.699Z
Learnt from: Flo4604
PR: unkeyed/unkey#3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as `description`) alongside `$ref` in schema objects. Do not flag this as an error in future reviews.

Applied to files:

  • go/apps/api/openapi/spec/common/KeyResponseData.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/createKey/V2KeysCreateKeyRequestBody.yaml
📚 Learning: 2025-08-19T09:46:03.702Z
Learnt from: Flo4604
PR: unkeyed/unkey#3800
File: go/internal/services/usagelimiter/redis.go:176-186
Timestamp: 2025-08-19T09:46:03.702Z
Learning: Zero-cost requests in the usage limiter should not change the remaining credits but should still return the actual remaining credits for that key, not treat the key as unlimited. A key with 100 credits remaining should still report 100 credits remaining after a zero-cost request.

Applied to files:

  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/index.yaml
  • go/apps/api/openapi/spec/common/Credits.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml
  • go/apps/api/openapi/spec/paths/v2/keys/createKey/V2KeysCreateKeyRequestBody.yaml
  • go/apps/api/openapi/openapi-generated.yaml
📚 Learning: 2025-08-21T15:54:45.198Z
Learnt from: chronark
PR: unkeyed/unkey#3825
File: go/internal/services/usagelimiter/limit.go:38-0
Timestamp: 2025-08-21T15:54:45.198Z
Learning: In go/internal/services/usagelimiter/limit.go, the UpdateKeyCreditsDecrement operation cannot be safely wrapped with db.WithRetry due to the lack of idempotency mechanisms in the current tech stack. Retrying this non-idempotent write operation risks double-charging users if the first attempt commits but the client sees a transient error.

Applied to files:

  • go/apps/api/openapi/spec/paths/v2/keys/updateCredits/index.yaml
  • go/apps/api/openapi/gen.go
  • go/apps/api/openapi/openapi-generated.yaml
📚 Learning: 2025-04-18T20:01:33.812Z
Learnt from: Flo4604
PR: unkeyed/unkey#3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For identity deletion operations in the Unkey API, identityId takes precedence over externalId when both are provided in the request body.

Applied to files:

  • go/apps/api/openapi/spec/paths/v2/identities/getIdentity/index.yaml
  • go/apps/api/openapi/openapi-generated.yaml
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
PR: unkeyed/unkey#3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, overlays are required to downconvert 3.1 union nullability to 3.0-style nullable before running oapi-codegen; config.yaml’s output-options.overlay is not recognized by oapi-codegen, so overlays must be applied in a pre-step (programmatic or CLI) prior to codegen.

Applied to files:

  • go/apps/api/openapi/overlay.yaml
  • go/apps/api/openapi/gen.go
📚 Learning: 2025-08-08T15:20:40.288Z
Learnt from: Flo4604
PR: unkeyed/unkey#3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:20:40.288Z
Learning: Repo unkeyed/unkey: oapi-codegen v2.4+ (v2.5.0 in use) supports output-options.overlay in go/apps/api/openapi/config.yaml; the generator applies overlay.yaml at codegen time, so no separate pre-step is required if oapi-codegen is invoked with -config=config.yaml.

Applied to files:

  • go/apps/api/openapi/overlay.yaml
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
PR: unkeyed/unkey#3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, oapi-codegen doesn’t support OpenAPI 3.1 union nullability; overlay.yaml must be applied before codegen. The overlay key in oapi-codegen config isn’t supported—use a pre-step (programmatic or CLI) to merge overlay into the bundled spec, then run oapi-codegen.

Applied to files:

  • go/apps/api/openapi/overlay.yaml
  • go/apps/api/openapi/gen.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
PR: unkeyed/unkey#3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.

Applied to files:

  • go/apps/api/openapi/gen.go
📚 Learning: 2025-03-19T09:25:59.751Z
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Applied to files:

  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/VerifyKeyIdentity.yaml
🔇 Additional comments (24)
go/apps/api/openapi/spec/common/CreditInterval.yaml (1)

1-6: Clean extraction of CreditInterval enum.

This is a well-structured, reusable enum schema that consolidates interval options (daily, monthly) across credits-related models. The example is valid.

go/apps/api/openapi/spec/common/Identity.yaml (1)

18-19: Identity credits field addition is properly scoped and optional.

The new credits field correctly references the consolidated Credits.yaml schema and is properly optional (not in the required list), allowing backward compatibility with identities that don't use credits.

Per learnings, input validation occurs at the OpenAPI schema level, so verify that the Credits.yaml nullability semantics are correct (especially for unlimited scenarios).

go/apps/api/openapi/spec/paths/v2/identities/getIdentity/index.yaml (1)

25-25: Example correction improves API documentation accuracy.

Changing the example from identity: user_123 to externalId: user_123 correctly reflects the actual request schema field name and reduces developer confusion.

go/apps/api/openapi/spec/common/CreditsRefill.yaml (1)

4-5: Schema extraction of CreditInterval reference is correctly applied.

Moving the interval definition from inline to a $ref to CreditInterval.yaml follows DRY principles and enables reuse across credit-related schemas. The refillDay description correctly documents the monthly interval dependency. Verification confirms CreditInterval.yaml enum values (daily, monthly) match the documented semantics.

go/apps/api/openapi/openapi-split.yaml (1)

140-141: Running additional verification to check the endpoint structure and consistency:<!-- [scratchpad_start]
[task review_file_1/1 verification_3/3]
Final verification of $ref usage and endpoint structure.
[observations]

  • OpenAPI specification confirms: $ref is allowed in Path Item Object (per web results 12-1, 19-11)
  • Reference can point to external files (per 15-7: "we decided to limit path item refs to external files only")
  • The pattern used: /v2/identities.updateCredits: $ref: "./spec/paths/v2/identities/updateCredits/index.yaml" is valid
  • File exists and YAML is valid (from previous script output)
    [analysis]
  • $ref in Path Item Object IS supported in OpenAPI spec (historical feature from 2.0)
  • External file reference is the proper use case
  • Structure is correct and follows best practice for modularized specs
  • No issues found with the implementation
    [pending]
  • None - sufficient verification completed
    [done]
  • File exists: Confirmed
  • YAML syntax valid: Confirmed
  • $ref pattern valid: Confirmed
  • No actionable issues identified
    [scratchpad_start] -->

No issues found with the identities.updateCredits endpoint addition.

The $ref URI correctly resolves to a Path Item Object, and external file references are the standard approach for path item reuse in OpenAPI. The file exists with valid YAML syntax, and the reference follows the established pattern in the codebase. The path addition is properly configured.

go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml (1)

79-84: Schema consolidation from KeyCreditsData to UpdateCredits verified and correct.

All referenced schemas (UpdateCredits.yaml and UpdateCreditsRefill.yaml) exist and are structurally valid. Examples at lines 148–152 and 168–172 are consistent with the schema definitions. The consolidation correctly uses update semantics for the request body.

go/apps/api/openapi/spec/paths/v2/keys/updateCredits/V2KeysUpdateCreditsResponseBody.yaml (1)

8-9: Schema consolidation to Credits is correct and fully verified.

The Credits schema properly supports all scenarios: remaining correctly allows null values for unlimited cases, and refill is defined as an optional reference to CreditsRefill.yaml. Response examples align with the schema structure.

go/apps/api/openapi/spec/common/KeyResponseData.yaml (1)

70-71: Schema consolidation verified: no issues found.

The credits field reference change from KeyCreditsData to Credits is correct. The Credits.yaml schema has proper nullability configuration:

  • remaining: nullable integer (correctly null for unlimited)
  • refill: optional object with required interval and amount fields

All schema structures validate correctly.

go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseData.yaml (1)

88-93: Reference change to VerifyKeyIdentity is well-motivated.

Moving from the generic Identity.yaml to a dedicated VerifyKeyIdentity.yaml schema appropriately specializes the identity payload for the verify key response context. The description update clearly conveys the identity's role in cross-device/cross-application scenarios.

go/apps/api/openapi/spec/common/CreditOperation.yaml (1)

1-8: Well-designed shared enum for credit operations.

Externalizing CreditOperation as a reusable schema eliminates duplication and clarifies the semantics of set, increment, and decrement across both key and identity credit update endpoints. The description is concise and actionable.

go/apps/api/openapi/spec/paths/v2/keys/updateCredits/index.yaml (1)

21-22: OperationId rename for API clarity.

Renaming from updateCredits to updateKeyCredits appropriately disambiguates key-level credit updates from the identity-level counterpart, improving API surface clarity for SDKs and client code. The Speakeasy override ensures consistent naming across generated artifacts.

go/apps/api/openapi/spec/common/Credits.yaml (1)

1-17: Consolidated Credits schema is well-structured.

Generalizing the description and renaming KeyCreditsRefill to CreditsRefill effectively removes key-specific assumptions, enabling this schema to serve both key-level and identity-level credit contexts. The addition of additionalProperties: false appropriately constrains the schema. Note that refill is optional, which correctly reflects the design (credits don't require a refill strategy).

go/apps/api/openapi/spec/paths/v2/keys/updateCredits/V2KeysUpdateCreditsRequestBody.yaml (1)

28-29: Operation externalized for reusability.

Moving the operation enum to CreditOperation.yaml follows the DRY principle and enables consistent reuse across both key and identity credit update endpoints. The request body structure and examples remain clear and comprehensive.

go/apps/api/openapi/overlay.yaml (1)

34-59: Overlay actions properly downconvert 3.1 union types to 3.0 nullable format.

The new overlay entries consistently apply the 3.1→3.0 nullability downconversion pattern for the consolidated credit schemas (Credits, UpdateCredits, UpdateCreditsRefill) and identity credit request body. Each entry correctly splits the type and nullable updates via separate JSONPath targets. The comment "Credits overlays (now consolidated)" helpfully marks this cohort of changes.

go/apps/api/openapi/spec/paths/v2/identities/updateCredits/V2IdentitiesUpdateCreditsRequestBody.yaml (2)

1-27: Identity credits update request body mirrors key structure with appropriate specialization.

The schema maintains excellent symmetry with V2KeysUpdateCreditsRequestBody while correctly specializing for identity context: identity field (accepting either externalId or identityId) replaces keyId, and the value/operation semantics are preserved. The dual acceptance of externalId/identityId improves usability for callers.


28-70: Examples are comprehensive and appropriately contextualized.

The six examples (planUpgrade, creditPurchase, unlimitedUpgrade, billingCycleReset, policyViolation, refundCredits) mirror the key-level examples but correctly illustrate identity-level operations with example identity IDs and descriptions referencing "keys associated with this identity." This makes the API contract clear for callers.

go/apps/api/openapi/spec/common/UpdateCreditsRefill.yaml (1)

1-27: LGTM – Good consolidation and nullability design.

The changes correctly externalize CreditInterval and make the refill config nullable to support disabling refills. The additionalProperties: false constraint is sound defensive design.

go/apps/api/openapi/spec/paths/v2/identities/createIdentity/V2IdentitiesCreateIdentityRequestBody.yaml (1)

48-56: LGTM – Clear semantics for identity-level credits.

The credits field correctly references Credits.yaml (full credit configuration) rather than UpdateCredits.yaml, which is the right design for creation. The description effectively communicates that identity credits are shared, take priority, and default to unlimited—key points for API consumers.

go/apps/api/openapi/spec/paths/v2/keys/verifyKey/VerifyKeyIdentity.yaml (1)

1-20: Verify field descriptions and integration with V2KeysVerifyKeyResponseData.

The schema correctly omits credits from the verification response (consistent with the prior design decision to avoid bloating responses). However, the field descriptions are notably terse compared to similar schemas:

  • meta: "Identity metadata" (vs. the detailed description in createIdentity)
  • ratelimits: "Identity ratelimits" (vs. the detailed description in createIdentity)

These read as placeholders. Please confirm whether this is intentional (minimal response documentation) or should be expanded to match the pattern elsewhere.

Also verify that this schema is actually wired into V2KeysVerifyKeyResponseData.identity as the replacement for the full Identity schema, since that usage context is not shown in the provided files.

go/apps/api/openapi/spec/paths/v2/identities/updateCredits/index.yaml (1)

1-70: LGTM – Well-structured identity credits update endpoint.

The endpoint definition is comprehensive: clear operation semantics, detailed description with use cases, proper security constraints with granular permissions, and well-documented side effects (unlimited credits auto-clearing refills). The error response coverage is complete.

go/apps/api/openapi/spec/paths/v2/identities/updateCredits/V2IdentitiesUpdateCreditsResponseBody.yaml (1)

1-56: LGTM – Clear response structure with excellent examples.

The response correctly references Credits.yaml to return full credit state. The four examples effectively demonstrate realistic scenarios:

  • Plan upgrades and purchases (with refill configs)
  • Unlimited mode transition (null remaining/refill)
  • Billing resets

The examples properly illustrate nullability semantics and will help API consumers understand the expected response structure.

go/apps/api/openapi/spec/common/UpdateCredits.yaml (1)

1-21: LGTM – Well-designed update schema with clear semantics.

The union type (object | null) correctly represents the update operation semantics: omit to preserve, null to disable. The remaining field properly supports both bounded int64 values and null for unlimited. Bounds match int64 limits, and additionalProperties: false prevents injection.

go/apps/api/openapi/openapi-generated.yaml (2)

360-369: Docs claim identity credits take precedence; verify rollout status.

Text says identity credits take priority during verification. Per retrieved learnings, priority enforcement in the usagelimiter was deferred to a later PR in the stack and not yet implemented. Add a note or feature flag marker to avoid overpromising until enforcement lands. Based on learnings.

Also applies to: 4000-4013


2252-2269: Nice consolidation of shared credit types.

Credits, CreditsRefill, CreditInterval, UpdateCredits, and UpdateCreditsRefill look consistent and future-proof. Good move away from key-specific variants.

Please regenerate SDKs to confirm no breaking changes in clients that previously consumed KeyCreditsData/UpdateKeyCreditsData names.

Also applies to: 2335-2360, 2445-2493

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants