Skip to content

add /auth/delegated-keys endpoints#574

Open
DhruvPareek wants to merge 1 commit into
dp/delegated-keys-schemasfrom
dp/delegated-keys-endpoints
Open

add /auth/delegated-keys endpoints#574
DhruvPareek wants to merge 1 commit into
dp/delegated-keys-schemasfrom
dp/delegated-keys-endpoints

Conversation

@DhruvPareek

@DhruvPareek DhruvPareek commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What

Adds the delegated signing key endpoints to the spec, wired to the schemas from the PR downstack:

Endpoint Purpose
POST /auth/delegated-keys Delegate Spark token-transaction signing authority on an embedded-wallet account to a Grid-custodied P-256 API key (three-leg signed-retry flow; Grid generates the keypair, the private key is never returned)
GET /auth/delegated-keys?accountId= List delegated keys, including PENDING and REVOKED
DELETE /auth/delegated-keys/{id} Revoke a delegated key (single signed-retry leg)

Includes the regenerated bundles (openapi.yaml, mintlify/openapi.yaml) via make build.

How the flows work

Delegation creates a non-root signer user holding a Grid-generated public key, then a policy granting that user raw-payload signing. The wallet owner alone constitutes the sub-org root quorum, so every activity must be stamped by the owner's session key — hence signed-retry legs mirroring DELETE /auth/credentials/{id}:

  • Create: 202 (step: CREATE_USER) → stamped retry → 202 (step: CREATE_POLICY) → stamped retry → 201 DelegatedKey (ACTIVE). Each account may hold at most one non-revoked key (409 otherwise). Abandoning mid-flow leaves a PENDING key that cannot sign.
  • Revoke: 202 (step: DELETE_USER) → stamped retry deletes the signer user and its API key204. That is the complete kill switch: signing stops because the credential is gone. The policy is intentionally left in place — its consensus references the deleted user (signer user IDs are never reused), so it is permanently inert and deleting it is unnecessary. DelegatedKeyChallengeStep accordingly has no DELETE_POLICY value.

After activation, Grid uses the custodied key to authorize signing on the account's behalf (e.g. funding card payments from the wallet balance) — the platform never handles key material and the quote-execute contract is unchanged.

Security notes

  • The private key is generated and custodied by Grid; it is never returned via any API.
  • A delegated key is full signing authority for the wallet: raw payloads are opaque to the signer's policy engine, so delegation cannot be scoped to amounts/recipients. Called out in the endpoint description.
  • Revocation requires the wallet owner's stamp; on completion the signer stops authenticating the key and Grid destroys its private-key copy.

Verification

  • make build — bundle regenerated; make lint — passes, no new findings

Notes for reviewers

  • Server-side implementation (sparkcore handlers) ships separately; a live-signer spike validating the policy condition syntax is planned before those land.
  • Mintlify docs pages for these endpoints are intentionally out of scope for this PR.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Jun 12, 2026 11:33pm

Request Review

DhruvPareek commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

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.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

chore(internal): regenerate SDK with no functional changes

csharp

chore(internal): regenerate SDK with no functional changes

go

chore(internal): regenerate SDK with no functional changes

kotlin

chore(internal): regenerate SDK with no functional changes

openapi

feat(api): add create/list/revoke delegated keys endpoints to auth

php

chore(internal): regenerate SDK with no functional changes

python

chore(internal): regenerate SDK with no functional changes

ruby

chore(internal): regenerate SDK with no functional changes

typescript

chore(internal): regenerate SDK with no functional changes

Edit this comment to update them. They will appear in their respective SDK's changelogs.

grid-openapi studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-ruby studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ⏭️ (prev: build ✅) → lint ✅test ✅

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-typescript studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ✅) → test ✅

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-go studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ⏭️ (prev: build ✅) → lint ❗test ❗

go get github.com/stainless-sdks/grid-go@78919ce02b584c3724b439d7f8b65930a7641c2d
New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-kotlin studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ✅) → test ✅

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-python studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ❗) → test ❗

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-csharp studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️build ⏭️ (prev: build ❗) → lint ⏭️ (prev: lint ✅) → test ❗

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-php studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅lint ✅test ✅

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`
grid-cli studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️build ❗lint ❗test ❗

New diagnostics (3 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `get /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /auth/delegated-keys`
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `delete /auth/delegated-keys/{id}`

This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-06-12 23:37:17 UTC

@DhruvPareek DhruvPareek marked this pull request as ready for review June 11, 2026 18:30
@DhruvPareek DhruvPareek requested a review from pengying June 11, 2026 18:30
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds three new OpenAPI endpoints for delegated signing key management (POST /auth/delegated-keys, GET /auth/delegated-keys, DELETE /auth/delegated-keys/{id}) along with associated schemas, regenerating both bundle files. The spec is internally consistent and the flows are clearly documented.

  • Create is a three-leg signed-retry flow (initial call → CREATE_USER stamp → CREATE_POLICY stamp → 201 ACTIVE); an abandoned mid-flow key lands in PENDING and cannot sign until revoked.
  • Revoke was simplified to a single-leg flow (DELETE_USER stamp → 204); DELETE_POLICY was intentionally dropped because the orphaned policy references a now-deleted Turnkey user ID and is permanently inert.
  • The PR description's "How the flows work" section still describes the old two-leg revocation (DELETE_USER + DELETE_POLICY), which no longer matches the spec — worth correcting before the server-side handler is implemented against this spec.

Confidence Score: 5/5

Safe to merge — the spec is internally consistent, schemas compose correctly via allOf, and the flows are well-documented with appropriate error codes for all error paths.

All changed files are OpenAPI spec additions with no runtime logic. The schemas are well-formed, the signed-retry pattern mirrors the existing credential revocation design, and all relevant edge cases (PENDING keys, expired challenges, conflict on duplicate active keys) are covered by documented response codes.

No files require special attention; the one note about the stale PR description does not affect the spec files themselves.

Important Files Changed

Filename Overview
openapi/paths/auth/auth_delegated-keys.yaml New file defining POST (three-leg create) and GET (list) for delegated keys; well-documented with 201/202/400/401/404/409/500 responses and examples.
openapi/paths/auth/auth_delegated-keys_{id}.yaml New file defining DELETE (single-leg revocation via DELETE_USER) with 202/204/400/401/404/500 responses; consistent with credential revocation pattern.
openapi/components/schemas/auth/DelegatedKeyChallengeStep.yaml Removes DELETE_POLICY from the enum, simplifying revocation to a single DELETE_USER leg; description updated to match.
openapi/openapi.yaml Adds two new path $ref entries for /auth/delegated-keys and /auth/delegated-keys/{id}.
openapi.yaml Regenerated bundle identical to mintlify/openapi.yaml; adds all delegated-key schemas and path operations inline.
mintlify/openapi.yaml Regenerated Mintlify bundle; content mirrors openapi.yaml with all delegated-key additions.

Sequence Diagram

sequenceDiagram
    participant C as Client
    participant G as Grid API

    rect rgb(220, 240, 255)
        Note over C,G: POST /auth/delegated-keys (Create — 3 legs)
        C->>G: "POST /auth/delegated-keys {accountId, nickname}"
        G-->>C: "202 {step: CREATE_USER, payloadToSign, requestId}"
        C->>G: POST + Grid-Wallet-Signature + Request-Id
        G-->>C: "202 {step: CREATE_POLICY, payloadToSign, requestId}"
        C->>G: POST + Grid-Wallet-Signature + Request-Id
        G-->>C: "201 DelegatedKey {status: ACTIVE}"
    end

    rect rgb(220, 255, 220)
        Note over C,G: GET /auth/delegated-keys?accountId= (List)
        C->>G: "GET /auth/delegated-keys?accountId=..."
        G-->>C: "200 {data: [DelegatedKey, ...]}"
    end

    rect rgb(255, 235, 220)
        Note over C,G: DELETE /auth/delegated-keys/{id} (Revoke — 1 leg)
        C->>G: "DELETE /auth/delegated-keys/{id}"
        G-->>C: "202 {step: DELETE_USER, payloadToSign, requestId}"
        C->>G: DELETE + Grid-Wallet-Signature + Request-Id
        G-->>C: 204 (key REVOKED, Turnkey user deleted)
    end
Loading

Reviews (6): Last reviewed commit: "feat(openapi): add /auth/delegated-keys ..." | Re-trigger Greptile

Comment thread openapi/paths/auth/auth_delegated-keys.yaml
Comment thread openapi/paths/auth/auth_delegated-keys.yaml Outdated
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-schemas branch from 357e5a5 to 6c65f64 Compare June 11, 2026 18:47
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from 91998d0 to 87f26f1 Compare June 11, 2026 18:47
@DhruvPareek DhruvPareek changed the title feat(openapi): add /auth/delegated-keys endpoints add /auth/delegated-keys endpoints Jun 11, 2026
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from 87f26f1 to 2b2e4d1 Compare June 11, 2026 23:21
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-schemas branch 2 times, most recently from d784a24 to de24d16 Compare June 11, 2026 23:48
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch 2 times, most recently from 910e44e to be20645 Compare June 12, 2026 21:34
POST /auth/delegated-keys (three-leg signed-retry creation),
GET /auth/delegated-keys (list), and DELETE /auth/delegated-keys/{id}
(signed-retry revocation, user-deletion first) for delegating Spark
token-transaction signing authority on an embedded wallet to a
platform-held P-256 API key.

Includes the regenerated bundles (openapi.yaml, mintlify/openapi.yaml)
via make build. make lint passes with no new findings.
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from be20645 to cc2e38f Compare June 12, 2026 23:33
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.

1 participant