Skip to content

Commit f67de9c

Browse files
feat: Let customers keep refreshing imported Link wallet grants
Stainless-Generated-From: 97ae6043e5c5ede83741602c06c8d6931c09fefd
1 parent c11d264 commit f67de9c

10 files changed

Lines changed: 319 additions & 33 deletions

‎src/kernel/resources/vaults/items.py‎

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,67 @@ def perform_operation(
14241424
"""
14251425
...
14261426

1427+
@overload
1428+
def perform_operation(
1429+
self,
1430+
key: str,
1431+
*,
1432+
id_or_name: str,
1433+
access_token: str,
1434+
type: Literal["update_access_token"],
1435+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
1436+
# The extra values given here take precedence over values defined on the client or passed to this method.
1437+
extra_headers: Headers | None = None,
1438+
extra_query: Query | None = None,
1439+
extra_body: Body | None = None,
1440+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
1441+
idempotency_key: str | None = None,
1442+
) -> VaultItemOperationResponse:
1443+
"""
1444+
Retrieve the item first and invoke only an operation listed in
1445+
`available_operations`, following its natural-language description. Availability
1446+
is rechecked at execution time; unavailable operations return 409. Authorization
1447+
and preparation may call an external provider and return updated state. Link
1448+
cards advertise authorize; pass browser_id and page_url from the final checkout
1449+
page so Kernel can inspect the checkout. Eligible unused AgentCard cards
1450+
advertise prepare_checkout, which requires checkout context and obtains device
1451+
approval before native Square Pay. Keep the returned approval page open, poll
1452+
until ready_to_submit, then submit before preparation.expires_at. Unused
1453+
preparations expire automatically and cannot be reused. If spend-request
1454+
creation is rejected with a non-retryable provider error, the card item is
1455+
deleted and the provider's error code and message are returned. Rate limits
1456+
return HTTP 429 and retain the card item; stop, back off, and retry the same
1457+
authorize operation.
1458+
1459+
Ready Kernel cards advertise fill. Fill delivers card details for a few minutes;
1460+
submit checkout promptly after filling. A repeated fill is safe.
1461+
1462+
Fill returns a value-free execution result. Validation failures before writing
1463+
return 400 (invalid request or targets), 403 (access or destination denied), 404
1464+
(resource not found), or 409 (item or browser not ready). Once writing starts,
1465+
known partial failures and indeterminate field outcomes return 200 with status
1466+
`failed` or `unknown`. Fill never submits the page, so it is safe to retry after
1467+
a failure, an `unknown` outcome, or a transport error.
1468+
1469+
Connected Kernel wallets may advertise add_payment_method, which returns a
1470+
single-use hosted card_enrollment link for a card, and remove_payment_method,
1471+
which removes one card without affecting the others. Both operations require
1472+
vaults:write in addition to vaults:use on the vault for scoped API keys, and
1473+
return 403 without both permissions.
1474+
1475+
Args:
1476+
extra_headers: Send extra headers
1477+
1478+
extra_query: Add additional query parameters to the request
1479+
1480+
extra_body: Add additional JSON properties to the request
1481+
1482+
timeout: Override the client-level default timeout for this request, in seconds
1483+
1484+
idempotency_key: Specify a custom idempotency key for this request
1485+
"""
1486+
...
1487+
14271488
@overload
14281489
def perform_operation(
14291490
self,
@@ -1656,6 +1717,7 @@ def perform_operation(
16561717
| Literal["1pw_fill"]
16571718
| Literal["1pw_recover"]
16581719
| Literal["1pw_update_access_token"]
1720+
| Literal["update_access_token"]
16591721
| Literal["webmcp_invoke"]
16601722
| Literal["add_payment_method"]
16611723
| Literal["remove_payment_method"],
@@ -3429,6 +3491,67 @@ async def perform_operation(
34293491
"""
34303492
...
34313493

3494+
@overload
3495+
async def perform_operation(
3496+
self,
3497+
key: str,
3498+
*,
3499+
id_or_name: str,
3500+
access_token: str,
3501+
type: Literal["update_access_token"],
3502+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
3503+
# The extra values given here take precedence over values defined on the client or passed to this method.
3504+
extra_headers: Headers | None = None,
3505+
extra_query: Query | None = None,
3506+
extra_body: Body | None = None,
3507+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
3508+
idempotency_key: str | None = None,
3509+
) -> VaultItemOperationResponse:
3510+
"""
3511+
Retrieve the item first and invoke only an operation listed in
3512+
`available_operations`, following its natural-language description. Availability
3513+
is rechecked at execution time; unavailable operations return 409. Authorization
3514+
and preparation may call an external provider and return updated state. Link
3515+
cards advertise authorize; pass browser_id and page_url from the final checkout
3516+
page so Kernel can inspect the checkout. Eligible unused AgentCard cards
3517+
advertise prepare_checkout, which requires checkout context and obtains device
3518+
approval before native Square Pay. Keep the returned approval page open, poll
3519+
until ready_to_submit, then submit before preparation.expires_at. Unused
3520+
preparations expire automatically and cannot be reused. If spend-request
3521+
creation is rejected with a non-retryable provider error, the card item is
3522+
deleted and the provider's error code and message are returned. Rate limits
3523+
return HTTP 429 and retain the card item; stop, back off, and retry the same
3524+
authorize operation.
3525+
3526+
Ready Kernel cards advertise fill. Fill delivers card details for a few minutes;
3527+
submit checkout promptly after filling. A repeated fill is safe.
3528+
3529+
Fill returns a value-free execution result. Validation failures before writing
3530+
return 400 (invalid request or targets), 403 (access or destination denied), 404
3531+
(resource not found), or 409 (item or browser not ready). Once writing starts,
3532+
known partial failures and indeterminate field outcomes return 200 with status
3533+
`failed` or `unknown`. Fill never submits the page, so it is safe to retry after
3534+
a failure, an `unknown` outcome, or a transport error.
3535+
3536+
Connected Kernel wallets may advertise add_payment_method, which returns a
3537+
single-use hosted card_enrollment link for a card, and remove_payment_method,
3538+
which removes one card without affecting the others. Both operations require
3539+
vaults:write in addition to vaults:use on the vault for scoped API keys, and
3540+
return 403 without both permissions.
3541+
3542+
Args:
3543+
extra_headers: Send extra headers
3544+
3545+
extra_query: Add additional query parameters to the request
3546+
3547+
extra_body: Add additional JSON properties to the request
3548+
3549+
timeout: Override the client-level default timeout for this request, in seconds
3550+
3551+
idempotency_key: Specify a custom idempotency key for this request
3552+
"""
3553+
...
3554+
34323555
@overload
34333556
async def perform_operation(
34343557
self,
@@ -3661,6 +3784,7 @@ async def perform_operation(
36613784
| Literal["1pw_fill"]
36623785
| Literal["1pw_recover"]
36633786
| Literal["1pw_update_access_token"]
3787+
| Literal["update_access_token"]
36643788
| Literal["webmcp_invoke"]
36653789
| Literal["add_payment_method"]
36663790
| Literal["remove_payment_method"],

‎src/kernel/types/vaults/credential_account_vault_item.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AvailableOperation(BaseModel):
4545
"1pw_fill",
4646
"1pw_recover",
4747
"1pw_update_access_token",
48+
"update_access_token",
4849
"webmcp_invoke",
4950
"add_payment_method",
5051
"remove_payment_method",

‎src/kernel/types/vaults/credential_vault_item.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class AvailableOperation(BaseModel):
5252
"1pw_fill",
5353
"1pw_recover",
5454
"1pw_update_access_token",
55+
"update_access_token",
5556
"webmcp_invoke",
5657
"add_payment_method",
5758
"remove_payment_method",

‎src/kernel/types/vaults/item_perform_operation_params.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"OnePasswordFillVaultItemOperationRequest",
2929
"OnePasswordRecoverVaultItemOperationRequest",
3030
"OnePasswordUpdateAccessTokenVaultItemOperationRequest",
31+
"UpdateAccessTokenVaultItemOperationRequest",
3132
"WebmcpInvokeVaultItemOperationRequest",
3233
"AddPaymentMethodVaultItemOperationRequest",
3334
"RemovePaymentMethodVaultItemOperationRequest",
@@ -267,6 +268,14 @@ class OnePasswordUpdateAccessTokenVaultItemOperationRequest(TypedDict, total=Fal
267268
"""Optional supplied expiry. Omit to clear the old expiry."""
268269

269270

271+
class UpdateAccessTokenVaultItemOperationRequest(TypedDict, total=False):
272+
id_or_name: Required[str]
273+
274+
access_token: Required[str]
275+
276+
type: Required[Literal["update_access_token"]]
277+
278+
270279
class WebmcpInvokeVaultItemOperationRequest(TypedDict, total=False):
271280
id_or_name: Required[str]
272281

@@ -330,6 +339,7 @@ class RemovePaymentMethodVaultItemOperationRequest(TypedDict, total=False):
330339
OnePasswordFillVaultItemOperationRequest,
331340
OnePasswordRecoverVaultItemOperationRequest,
332341
OnePasswordUpdateAccessTokenVaultItemOperationRequest,
342+
UpdateAccessTokenVaultItemOperationRequest,
333343
WebmcpInvokeVaultItemOperationRequest,
334344
AddPaymentMethodVaultItemOperationRequest,
335345
RemovePaymentMethodVaultItemOperationRequest,

‎src/kernel/types/vaults/item_upsert_params.py‎

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,15 @@ class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImpor
107107
class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImportedLinkAuthorizationInputTokens(
108108
TypedDict, total=False
109109
):
110-
"""Send the token pair from your backend.
110+
"""Send the tokens from your backend.
111111
112-
Both tokens must be from the same Link grant under the referenced client. Supply a currently valid access token. Kernel refreshes when needed after import and uses the expiry returned by Link for subsequent tokens. Tokens are never returned in wallet responses, events, or logs.
112+
Supply a currently valid access token. With token_refresh=kernel, also send the refresh token from the same Link grant under the referenced client; Kernel refreshes when needed after import and uses the expiry returned by Link for subsequent tokens. With token_refresh=customer, omit the refresh token. Tokens are never returned in wallet responses, events, or logs.
113113
"""
114114

115115
access_token: Required[str]
116116

117-
refresh_token: Required[str]
117+
refresh_token: str
118+
"""Required with token_refresh=kernel; rejected with token_refresh=customer."""
118119

119120

120121
class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImportedLinkAuthorizationInput(
@@ -123,7 +124,8 @@ class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImpor
123124
"""The customer's backend completes Link OAuth and supplies the resulting tokens.
124125
125126
For a new wallet, Kernel verifies the access token can access Link payment methods without consuming or rotating the refresh token. Valid access creates a wallet with state.status=connected. An expired, invalid, revoked, or insufficiently scoped access token returns 400 and no wallet is created. Refresh expired tokens in your backend before importing them. A failed import does not modify existing wallets.
126-
After successful import, Kernel owns subsequent refresh-token rotation; the customer must stop refreshing this grant. Import does not verify the refresh token: if it or the configured client credentials are rejected during a later refresh, the imported wallet becomes degraded. An unknown refresh outcome also leaves it degraded; Kernel does not retry a refresh token that may already have been consumed. There is no in-place reauthorization operation for an imported wallet.
127+
By default (token_refresh=kernel), Kernel owns subsequent refresh-token rotation after a successful import; the customer must stop refreshing this grant. With token_refresh=customer, send only the access token: your backend keeps the refresh token and rotates the grant, and Kernel never refreshes or revokes it. Replace the stored access token with the wallet's update_access_token operation before each step that reaches Link, such as creating, authorizing or filling a card. When Link rejects the stored access token, operations return 409 without contacting Link again; update the token and retry. The remainder of this description applies to token_refresh=kernel.
128+
With token_refresh=kernel, Kernel owns subsequent refresh-token rotation; the customer must stop refreshing this grant. Import does not verify the refresh token: if it or the configured client credentials are rejected during a later refresh, the imported wallet becomes degraded. An unknown refresh outcome also leaves it degraded; Kernel does not retry a refresh token that may already have been consumed. There is no in-place reauthorization operation for an imported wallet.
127129
If this imported wallet's credentials become unusable, obtain a fresh Link OAuth grant in your backend and create a wallet under a NEW wallet key. Use the new wallet for NEW cards and payments, not to retry an old payment whose outcome is uncertain. This does not replace the old grant, rebind existing cards, or resolve their payment outcomes. Retain the old wallet and its cards while reconciling any uncertain payments with the provider or support. Do not repeat an uncertain payment on the new wallet, and do not treat deletion as evidence that it did not execute. Deletion of the old wallet can remain blocked by unresolved child cards.
128130
Repeating a create for the same item key and non-secret spec returns the existing wallet without replacing tokens, even if they have rotated or the wallet needs reconnection. ID and name references resolving to the same config are equivalent. A different config or non-secret spec returns 409. This create operation does not replace an existing grant.
129131
"""
@@ -137,12 +139,22 @@ class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImpor
137139
tokens: Required[
138140
WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImportedLinkAuthorizationInputTokens
139141
]
140-
"""Send the token pair from your backend.
142+
"""Send the tokens from your backend.
141143
142-
Both tokens must be from the same Link grant under the referenced client. Supply
143-
a currently valid access token. Kernel refreshes when needed after import and
144-
uses the expiry returned by Link for subsequent tokens. Tokens are never
145-
returned in wallet responses, events, or logs.
144+
Supply a currently valid access token. With token_refresh=kernel, also send the
145+
refresh token from the same Link grant under the referenced client; Kernel
146+
refreshes when needed after import and uses the expiry returned by Link for
147+
subsequent tokens. With token_refresh=customer, omit the refresh token. Tokens
148+
are never returned in wallet responses, events, or logs.
149+
"""
150+
151+
token_refresh: Literal["kernel", "customer"]
152+
"""Who refreshes an imported Link grant.
153+
154+
kernel (default) means Kernel holds and rotates the refresh token. customer
155+
means your backend keeps the refresh token and replaces the access token with
156+
update_access_token; Kernel never refreshes or revokes the grant. Fixed at
157+
wallet creation.
146158
"""
147159

148160

‎src/kernel/types/vaults/payment_request_vault_item.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AvailableOperation(BaseModel):
4545
"1pw_fill",
4646
"1pw_recover",
4747
"1pw_update_access_token",
48+
"update_access_token",
4849
"webmcp_invoke",
4950
"add_payment_method",
5051
"remove_payment_method",

‎src/kernel/types/vaults/vault_item.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class WalletVaultItemAvailableOperation(BaseModel):
6363
"1pw_fill",
6464
"1pw_recover",
6565
"1pw_update_access_token",
66+
"update_access_token",
6667
"webmcp_invoke",
6768
"add_payment_method",
6869
"remove_payment_method",
@@ -170,6 +171,7 @@ class CardVaultItemAvailableOperation(BaseModel):
170171
"1pw_fill",
171172
"1pw_recover",
172173
"1pw_update_access_token",
174+
"update_access_token",
173175
"webmcp_invoke",
174176
"add_payment_method",
175177
"remove_payment_method",

‎src/kernel/types/vaults/vault_item_operation_response.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class WalletVaultItemAvailableOperation(BaseModel):
6868
"1pw_fill",
6969
"1pw_recover",
7070
"1pw_update_access_token",
71+
"update_access_token",
7172
"webmcp_invoke",
7273
"add_payment_method",
7374
"remove_payment_method",
@@ -175,6 +176,7 @@ class CardVaultItemAvailableOperation(BaseModel):
175176
"1pw_fill",
176177
"1pw_recover",
177178
"1pw_update_access_token",
179+
"update_access_token",
178180
"webmcp_invoke",
179181
"add_payment_method",
180182
"remove_payment_method",

‎src/kernel/types/vaults/wallet_vault_item_spec.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ class LinkWalletVaultItemSpecAuthorization(BaseModel):
6161

6262
method: Literal["oauth"]
6363

64+
token_refresh: Optional[Literal["kernel", "customer"]] = None
65+
"""Who refreshes an imported Link grant.
66+
67+
kernel (default) means Kernel holds and rotates the refresh token. customer
68+
means your backend keeps the refresh token and replaces the access token with
69+
update_access_token; Kernel never refreshes or revokes the grant. Fixed at
70+
wallet creation.
71+
"""
72+
6473

6574
class LinkWalletVaultItemSpec(BaseModel):
6675
authorization: LinkWalletVaultItemSpecAuthorization

0 commit comments

Comments
 (0)