Delegations
Allow your backend to sign transactions on behalf of users, with policy controls over chains, contracts, values, and time windows.
How it works
A delegation authorizes Kevo to accept backend-initiated sign requests for a user after that user opts in. Unlike the old browser-share model, granting a delegation does not upload wallet material from the iframe.
1. User grants delegation
The user calls grantDelegation() from your app. Kevo stores delegation metadata and policy constraints for that project-scoped user.
2. Backend signs through KevoAdmin
Your backend calls admin.delegations.signEvm() or admin.delegations.signSolana(). Kevo checks policies first, then routes the request through the server-side signing path.
3. User can revoke at any time
Calling revokeDelegation() invalidates future backend sign requests immediately.
grantDelegation() again replaces the previous delegation and its policies.Granting a delegation
React hook
import { useKevo } from '@kevo-ws/sdk/react'
function DelegationButton() {
const { grantDelegation, getDelegation, revokeDelegation } = useKevo()
async function handleGrant() {
const delegation = await grantDelegation({
include: 'both',
policies: {
expiresAt: '2026-12-31T00:00:00.000Z',
maxTxCount: 200,
allowedChainIds: [8453, 1],
allowedContracts: ['0xYourContract...'],
maxAmountWei: '1000000000000000000',
},
})
console.log('Delegation active, txCount:', delegation.txCount)
}
return (
<div>
<button onClick={handleGrant}>Enable auto-signing</button>
<button onClick={() => getDelegation().then(console.log)}>Check status</button>
<button onClick={revokeDelegation}>Revoke</button>
</div>
)
}Direct client call (without React)
import { KevoClient } from '@kevo-ws/sdk'
const client = new KevoClient({ publishableKey: 'pk_live_...' })
const delegation = await client.grantDelegation({
include: 'evm',
policies: { maxTxCount: 50 },
})
const current = await client.getDelegation()
await client.revokeDelegation()Client-side REST endpoints
/v1/wallets/me/delegationBearer (user)Returns the current user's active delegation, or null.
/v1/wallets/me/delegation/grantBearer (user)Grant or replace a delegation.
POST /v1/wallets/me/delegation/grant
Authorization: Bearer <user_access_token>
Content-Type: application/json
{
"include": "both",
"policies": {
"expiresAt": "2026-12-31T00:00:00.000Z",
"maxTxCount": 500
}
}/v1/wallets/me/delegation/revokeBearer (user)Revoke the active delegation permanently.
Delegation policies
interface GrantDelegationOptions {
include?: 'evm' | 'solana' | 'both'
policies?: {
expiresAt?: string
maxTxCount?: number
allowedChainIds?: number[]
allowedContracts?: string[]
maxAmountWei?: string
}
}| Field | Type | Description |
|---|---|---|
expiresAt | ISO 8601 string | Hard expiry. Signing attempts after this datetime return 403. |
maxTxCount | number | Max successful signatures allowed. |
allowedChainIds | number[] | EVM chain ID whitelist. Enforced when chainId is provided. |
allowedContracts | string[] | EVM contract address whitelist, case-insensitive. |
maxAmountWei | decimal string | Max native value per EVM transaction in wei. |
Security notes
No browser wallet-material upload
The delegation flow no longer depends on extracting wallet material from the iframe.
Policy checks before signing
Chain, contract, amount, count, and expiry checks run before the signer is reached.
Metadata-only responses
Delegation list and detail endpoints return policy metadata, never raw wallet material.