Skip to content

add card-scoped /auth/delegated-keys endpoints#574

Merged
DhruvPareek merged 1 commit into
mainfrom
dp/delegated-keys-endpoints
Jun 18, 2026
Merged

add card-scoped /auth/delegated-keys endpoints#574
DhruvPareek merged 1 commit into
mainfrom
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 for a card funded by an Embedded Wallet internal 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=&cardId= List delegated keys by account, by card, or both, 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: client sends identical {cardId, nickname} on every leg. Initial call returns 202 with payloadToSign, requestId, and expiresAt; stamped retry returns a second 202 challenge; stamped retry of that challenge returns 201 DelegatedKey (ACTIVE). Grid derives the Embedded Wallet account from the card's funding sources. Each card may hold at most one non-revoked key (409 otherwise); multiple cards can share the same funding account and still have distinct delegated keys. Abandoning mid-flow leaves a PENDING key that cannot sign.
  • Revoke: initial call returns 202 with payloadToSign, requestId, and expiresAt; stamped retry deletes the signer user and its API key and returns 204. 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.

The public 202 challenge shape is signer-agnostic. Clients sign payloadToSign and retry with Grid-Wallet-Signature and Request-Id; Grid tracks the pending signer activity internally.

After activation, Grid uses the card's custodied key to authorize signing for card-payment funding 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 yet.
  • 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
  • make lint-openapi

Notes for reviewers

  • Server-side implementation (sparkcore handlers) ships separately; live-signer policy syntax validation remains a rollout prerequisite.
  • Mintlify docs pages for these endpoints are intentionally out of scope for this PR.

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Jun 18, 2026 6:03pm
grid-wallet-demo Ignored Ignored Preview Jun 18, 2026 6:03pm

Request Review

DhruvPareek commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

@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
grid-openapi studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅

grid-ruby studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ⏭️lint ✅test ✅

⚠️ grid-go studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ⏭️lint ❗test ❗

go get github.com/stainless-sdks/grid-go@b255ad753c3ae0622f2afe2e9dd1c4a9009d7aa5
grid-kotlin studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ⏭️lint ⏭️test ✅

⚠️ grid-python studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ✅build ⏭️lint ⏭️test ❗

⚠️ grid-csharp studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ❗

grid-php studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅lint ✅test ✅

grid-typescript studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ⏭️lint ⏭️test ✅

⚠️ grid-cli studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ❗


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-18 18:16:29 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

Adds three card-scoped delegated signing key endpoints (POST /auth/delegated-keys, GET /auth/delegated-keys, DELETE /auth/delegated-keys/{id}) along with their supporting schemas and the regenerated bundles. The three-leg signed-retry create flow and single-leg revoke flow are well-documented and consistent with the existing credential/session patterns.

  • POST create uses a three-leg signed-retry; the 409 correctly covers both ACTIVE and PENDING keys, and previous reviewer feedback on the 409 and GET 200 empty-array semantics has been incorporated.
  • GET list filters by accountId, cardId, or both, returning all statuses including PENDING and REVOKED; silently returns empty array for unknown IDs, matching GET /auth/credentials behaviour.
  • DELETE revoke follows the same single-leg signed-retry pattern as DELETE /auth/credentials/{id} and correctly documents the kill-switch semantics (user deleted, policy left inert).

Confidence Score: 5/5

Safe to merge — this is a pure OpenAPI spec addition with no application code; the three-leg create and single-leg revoke flows are correctly modelled and consistent with existing credential patterns.

The new path and schema files correctly capture the signed-retry flow, error codes, and status semantics. Previous review feedback has been addressed. The only open items are minor documentation gaps that do not affect correctness or security of the contract.

No files require special attention.

Important Files Changed

Filename Overview
openapi/paths/auth/auth_delegated-keys.yaml New POST (three-leg signed-retry create) and GET (list by accountId/cardId) endpoints; well-documented flow with accurate 409/401/404 responses and prior review feedback incorporated
openapi/paths/auth/auth_delegated-keys_{id}.yaml New GET-by-id and DELETE (single signed-retry revoke) endpoints; consistent with credential revocation patterns, though GET 200 description is minimal
openapi/components/schemas/auth/DelegatedKey.yaml Minor description update to add GET-by-id reference; required fields and property types look correct
openapi/openapi.yaml Adds two path $refs for the new delegated-keys endpoints; no issues

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Client
    participant G as Grid API
    participant T as Turnkey

    Note over C,T: POST /auth/delegated-keys (3-leg create)
    C->>G: "POST /auth/delegated-keys {cardId, nickname}"
    G->>T: Generate P-256 keypair
    T-->>G: keypair
    G-->>C: "202 {payloadToSign, requestId, expiresAt}"

    C->>G: POST /auth/delegated-keys + Grid-Wallet-Signature + Request-Id
    G->>T: Create delegated user (PENDING)
    T-->>G: user created
    G-->>C: "202 {payloadToSign, requestId, expiresAt}"

    C->>G: POST /auth/delegated-keys + Grid-Wallet-Signature + Request-Id
    G->>T: Grant signing policy
    T-->>G: policy granted
    G-->>C: "201 DelegatedKey {status: ACTIVE}"

    Note over C,T: DELETE /auth/delegated-keys/{id} (1-leg revoke)
    C->>G: "DELETE /auth/delegated-keys/{id}"
    G-->>C: "202 {payloadToSign, requestId, expiresAt}"

    C->>G: "DELETE /auth/delegated-keys/{id} + Grid-Wallet-Signature + Request-Id"
    G->>T: Delete delegated user + API key
    T-->>G: deleted
    G-->>C: 204 No Content
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C as Client
    participant G as Grid API
    participant T as Turnkey

    Note over C,T: POST /auth/delegated-keys (3-leg create)
    C->>G: "POST /auth/delegated-keys {cardId, nickname}"
    G->>T: Generate P-256 keypair
    T-->>G: keypair
    G-->>C: "202 {payloadToSign, requestId, expiresAt}"

    C->>G: POST /auth/delegated-keys + Grid-Wallet-Signature + Request-Id
    G->>T: Create delegated user (PENDING)
    T-->>G: user created
    G-->>C: "202 {payloadToSign, requestId, expiresAt}"

    C->>G: POST /auth/delegated-keys + Grid-Wallet-Signature + Request-Id
    G->>T: Grant signing policy
    T-->>G: policy granted
    G-->>C: "201 DelegatedKey {status: ACTIVE}"

    Note over C,T: DELETE /auth/delegated-keys/{id} (1-leg revoke)
    C->>G: "DELETE /auth/delegated-keys/{id}"
    G-->>C: "202 {payloadToSign, requestId, expiresAt}"

    C->>G: "DELETE /auth/delegated-keys/{id} + Grid-Wallet-Signature + Request-Id"
    G->>T: Delete delegated user + API key
    T-->>G: deleted
    G-->>C: 204 No Content
Loading

Reviews (13): 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 3 times, most recently from be20645 to cc2e38f Compare June 12, 2026 23:33
@DhruvPareek DhruvPareek marked this pull request as draft June 15, 2026 17:57
@DhruvPareek DhruvPareek marked this pull request as ready for review June 15, 2026 23:07
@DhruvPareek DhruvPareek removed the request for review from pengying June 16, 2026 22:37
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-schemas branch from de24d16 to d74182d Compare June 16, 2026 22:41
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from cc2e38f to 951aca3 Compare June 16, 2026 22:41
@DhruvPareek DhruvPareek changed the title add /auth/delegated-keys endpoints add card-scoped /auth/delegated-keys endpoints Jun 17, 2026
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from 951aca3 to a5a89b7 Compare June 17, 2026 01:27
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-schemas branch from d74182d to 972891c Compare June 17, 2026 01:27
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from a5a89b7 to b8e1af2 Compare June 17, 2026 20:12
@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-schemas branch from 972891c to 854df42 Compare June 17, 2026 20:12
@DhruvPareek DhruvPareek requested a review from shreyav June 17, 2026 21:08
@@ -0,0 +1,102 @@
delete:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could we also add a get by id?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yea lemme add

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added!

@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from b8e1af2 to f0d1d07 Compare June 17, 2026 21:36
shreyav
shreyav previously approved these changes Jun 17, 2026
@DhruvPareek DhruvPareek changed the base branch from dp/delegated-keys-schemas to graphite-base/574 June 18, 2026 18:02
@DhruvPareek DhruvPareek changed the base branch from graphite-base/574 to main June 18, 2026 18:03
@DhruvPareek DhruvPareek dismissed shreyav’s stale review June 18, 2026 18:03

The base branch was changed.

@DhruvPareek DhruvPareek force-pushed the dp/delegated-keys-endpoints branch from f0d1d07 to bdc0231 Compare June 18, 2026 18:03

@restamp-bot restamp-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bdc0231 is a pure rebase onto d55ccb4. Approving based on @shreyav's previous approval of f0d1d07.

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 bdc0231 to 8897e44 Compare June 18, 2026 18:03

@restamp-bot restamp-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

8897e44 is a pure rebase onto d55ccb4. Approving based on @shreyav's previous approval of f0d1d07.

@DhruvPareek DhruvPareek merged commit ea40e01 into main Jun 18, 2026
10 checks passed

Copy link
Copy Markdown
Contributor Author

Merge activity

@DhruvPareek DhruvPareek deleted the dp/delegated-keys-endpoints branch June 18, 2026 18:10
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