- 
                Notifications
    You must be signed in to change notification settings 
- Fork 585
feat: add identity-based credits OpenAPI specifications #4192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/identity-credits-api-v1
Are you sure you want to change the base?
feat: add identity-based credits OpenAPI specifications #4192
Conversation
| 
 | 
| The latest updates on your projects. Learn more about Vercel for GitHub. 
 | 
| Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite. 
 This stack of pull requests is managed by Graphite. Learn more about stacking. | 
| 📝 WalkthroughWalkthroughThis 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
 Sequence DiagramsequenceDiagram
    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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas requiring extra attention: 
 Possibly related PRs
 Suggested labels
 Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
 ✅ Passed checks (1 passed)
 ✨ Finishing touches
 🧪 Generate unit tests (beta)
 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. Comment  | 
ce27d21    to
    19aadaf      
    Compare
  
    19aadaf    to
    03872fb      
    Compare
  
    c300f90    to
    aa1950a      
    Compare
  
    03872fb    to
    6816e5e      
    Compare
  
    aa1950a    to
    baf6928      
    Compare
  
    6816e5e    to
    c199c71      
    Compare
  
    c199c71    to
    7397a54      
    Compare
  
    3369e55    to
    8d4a3a4      
    Compare
  
    35713ad    to
    552692d      
    Compare
  
    de5747f    to
    aa0e8f6      
    Compare
  
    aa0e8f6    to
    01d2ed1      
    Compare
  
    - 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
01d2ed1    to
    a086941      
    Compare
  
    0f0c40f    to
    775d35a      
    Compare
  
    | @@ -0,0 +1,20 @@ | |||
| type: object | |||
There was a problem hiding this comment.
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.
| @coderabbitai review | 
| ✅ Actions performedReview triggered. 
 | 
There was a problem hiding this 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 exposesCreditsas*int32, yet every other credits payload in this refactor moved toint64. 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 newCreditsmodel) 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: nullfor unlimited usage. While this may be enforced at the handler level, consider rewording to clarify that only the entirecreditsfield 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.yamland remains optional so omitting it preserves existing credits. This follows the same pattern as themetaandratelimitsfields above it.Minor suggestion: Consider adding an inline description for the credits field to match the pattern of
metaandratelimits(which have field-level documentation). The current design relies entirely on theUpdateCredits.yamlschema'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
📒 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
creditsfield correctly references the consolidatedCredits.yamlschema 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.yamlnullability 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_123toexternalId: user_123correctly 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
intervaldefinition from inline to a$reftoCreditInterval.yamlfollows DRY principles and enables reuse across credit-related schemas. The refillDay description correctly documents the monthly interval dependency. Verification confirmsCreditInterval.yamlenum 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.yamlandUpdateCreditsRefill.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
Creditsschema properly supports all scenarios:remainingcorrectly allows null values for unlimited cases, andrefillis defined as an optional reference toCreditsRefill.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
creditsfield reference change fromKeyCreditsDatatoCreditsis correct. TheCredits.yamlschema has proper nullability configuration:
remaining: nullable integer (correctlynullfor unlimited)
refill: optional object with requiredintervalandamountfieldsAll 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.yamlto a dedicatedVerifyKeyIdentity.yamlschema 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
CreditOperationas a reusable schema eliminates duplication and clarifies the semantics ofset,increment, anddecrementacross 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
updateCreditstoupdateKeyCreditsappropriately 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
KeyCreditsRefilltoCreditsRefilleffectively removes key-specific assumptions, enabling this schema to serve both key-level and identity-level credit contexts. The addition ofadditionalProperties: falseappropriately constrains the schema. Note thatrefillis 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.yamlfollows 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
V2KeysUpdateCreditsRequestBodywhile correctly specializing for identity context:identityfield (accepting either externalId or identityId) replaceskeyId, 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
CreditIntervaland make the refill config nullable to support disabling refills. TheadditionalProperties: falseconstraint 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 thanUpdateCredits.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 increateIdentity)
ratelimits: "Identity ratelimits" (vs. the detailed description increateIdentity)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.identityas the replacement for the fullIdentityschema, 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.yamlto 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. Theremainingfield properly supports both bounded int64 values and null for unlimited. Bounds match int64 limits, andadditionalProperties: falseprevents 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

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
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
pnpm buildpnpm fmtconsole.logsgit pull origin mainAppreciated