Delegated Keys
Allow delegated keys to obtain partial or full control of the wallet
Crossmint wallets can be controlled by more than one signer. This feature is useful when you have wallets that require flexible custodial architectures, such as:
- Company treasury wallets managed by multiple employees
- AI agent wallets, where the agent owner needs a key, and the agent itself needs another one
- Neobanks and other financial applications where the user is self-custodial but opts into allowing the bank to pull funds (e.g. for debit card)
A key characteristic in delegated signers is that they can be scoped to limit the permissions they have over the wallet, for example with spend or asset classes restrictions.
A Signer
refers to any private/public key pair that can cryptographically sign messages. This includes crypto wallets,
passkeys, hardware security modules (HSMs), and other signing mechanisms that can prove ownership or authorization
through digital signatures.
Key Characteristics
- 🔐 Enabled Delegated Access Securely: Allow third parties to control a wallet, with scoped permissions
- ⚡ Autonomous Operations: Enable AI agents to execute independently over a wallet
- 🎯 Granular Control: Set permissions and expiration dates for each delegated keys capabilities
- 🔑 Hybrid Custody: Keep a wallet non custodial for currencies, but allow your app’s backend to manage its non-regulated assets (such as utility tokens or NFTs)
How Delegated Signers Work
- Enroll: The admin signer for the wallet sends a transaction to add a delegated signer to the wallet, optionally specifying a set of restrictions
- Transact: The delegated signer can now perform transactions
- Unenroll: The admin signer can later revoke access to any delegated signer
Technical Implementation
In this quickstart, we propose a scenario where there’s a smart wallet that is owned by two keys: an admin key held by the wallet owner, and a delegated key given to an AI agent, with a restricted set of permissions.
Integration Steps
Create and Configure a Crossmint Project
To get started, create a developer account in the Crossmint Staging Console. Open that link, sign in, and accept the dialog to continue.
Crossmint offers two consoles: staging, for development and testing, and production.
Get an API Key
Once you log in to the console, the next step is to create an API key.
- Navigate to project Integrate > API Keys, and click the “Create new key” button in the “Server-side keys” section
- Under the Wallets API category, select the scopes
wallets.create
,wallets.read
,wallets:signatures.create
,wallets:transactions.create
,wallets:transactions.read
, andwallets:transactions.sign
- Click “Create server key” to save the key for the next step
For all requests to the Crossmint API, add the following headers:
Create a Delegated Key and Send a Transaction
Create a Smart Wallet
First, we create a Smart Wallet, controlled by the developer’s key, which will be called the admin signer
.
You can use a webauthn passkey as admin signer, an existing ethereum address, or generate an ethereum wallet in code.
In this quickstart we will generate the admin signer key in code:
This address is not the wallet you’ll put funds in. It’s just a key that will be used to control the smart wallet, which we create below.
Save the private key for the admin signer securely, you’ll use it later to approve a transaction.
Now, we are going to create a Smart Wallet and set its owner to be the admin key we just created.
To do so, we call the Create wallet endpoint with the following body:
With this, we have a smart wallet created, and it can be controlled by the admin (agent owner), 0x234
.
Create a Key for the Agent
As a next step, we want to create a separate key that will be held by our agent. This key will be able to send transactions on behalf of the smart wallet, but will have some restrictions attached.
For this tutorial, we will generate the agent key following the same steps as the admin key:
Then we should store the private key somewhere safe that the agent can access. For example, as an environment variable in the server that the agent is running on.
And now we register the address above as a delegated signer for the smart wallet, using the address locator returned when we created the wallet:
Approve Registration of Agent Signer with the Admin Key
The admin signer needs to approve the registration of the agent’s key as a delegated signer, by signing the message from the response, i.e. 0xme55ageTo5ign
- note that the response above is “awaiting-approval”.
We now need to submit that signed message using the signature approval endpoint, POST https://staging.crossmint.com/api/2022-06-09/wallets/0xdeadbeef/signatures/1bc61c9d-0929-4782-a21c-177acda93a2d/approvals
and pass the following body:
And with this, the agent key now has delegated access to the smart wallet 🎉
Send a Transaction from the Agent
Now that the agent’s key is approved, it can create transactions on the developer’s behalf, as long as transactions respect the defined permissions.
Here we are going to make a USDC transfer. The first step is to prepare the transaction:
Now we will use the transaction data generated and the Create Transaction API to submit a new transaction for the smart wallet with the following body:
Approve Transaction
The agent, via its delegated key, signs the transaction message to approve it. Then, it will be broadcasted onchain.
Like before, the message to sign is “awaiting-approval” in the previous response.
To submit the transaction, use the Transaction Approval API to pass the signature in the body, the transactionId from Step 4 in the route’s path, and the wallet’s locator, 0xdeadbeef
:
With this, our agent has already submitted its first transaction! 🕺
(Bonus) Check transactions for a wallet
The agent developer can easily fetch transactions executed by their wallet, as well as filter the responses based on signer to audit transactions by specific agents.
Create a Smart Wallet
First, we create a Smart Wallet, controlled by the developer’s key, which will be called the admin signer
.
You can use a webauthn passkey as admin signer, an existing ethereum address, or generate an ethereum wallet in code.
In this quickstart we will generate the admin signer key in code:
This address is not the wallet you’ll put funds in. It’s just a key that will be used to control the smart wallet, which we create below.
Save the private key for the admin signer securely, you’ll use it later to approve a transaction.
Now, we are going to create a Smart Wallet and set its owner to be the admin key we just created.
To do so, we call the Create wallet endpoint with the following body:
With this, we have a smart wallet created, and it can be controlled by the admin (agent owner), 0x234
.
Create a Key for the Agent
As a next step, we want to create a separate key that will be held by our agent. This key will be able to send transactions on behalf of the smart wallet, but will have some restrictions attached.
For this tutorial, we will generate the agent key following the same steps as the admin key:
Then we should store the private key somewhere safe that the agent can access. For example, as an environment variable in the server that the agent is running on.
And now we register the address above as a delegated signer for the smart wallet, using the address locator returned when we created the wallet:
Approve Registration of Agent Signer with the Admin Key
The admin signer needs to approve the registration of the agent’s key as a delegated signer, by signing the message from the response, i.e. 0xme55ageTo5ign
- note that the response above is “awaiting-approval”.
We now need to submit that signed message using the signature approval endpoint, POST https://staging.crossmint.com/api/2022-06-09/wallets/0xdeadbeef/signatures/1bc61c9d-0929-4782-a21c-177acda93a2d/approvals
and pass the following body:
And with this, the agent key now has delegated access to the smart wallet 🎉
Send a Transaction from the Agent
Now that the agent’s key is approved, it can create transactions on the developer’s behalf, as long as transactions respect the defined permissions.
Here we are going to make a USDC transfer. The first step is to prepare the transaction:
Now we will use the transaction data generated and the Create Transaction API to submit a new transaction for the smart wallet with the following body:
Approve Transaction
The agent, via its delegated key, signs the transaction message to approve it. Then, it will be broadcasted onchain.
Like before, the message to sign is “awaiting-approval” in the previous response.
To submit the transaction, use the Transaction Approval API to pass the signature in the body, the transactionId from Step 4 in the route’s path, and the wallet’s locator, 0xdeadbeef
:
With this, our agent has already submitted its first transaction! 🕺
(Bonus) Check transactions for a wallet
The agent developer can easily fetch transactions executed by their wallet, as well as filter the responses based on signer to audit transactions by specific agents.
Create a Smart Wallet
First, we create a Smart Wallet, controlled by the developer’s key, which will be called the admin signer
.
This address is not the wallet you’ll put funds in. It’s just a key that will be used to control the smart wallet, which we create below.
Save the private key for the admin signer securely.
Now, we are going to create a Smart Wallet and set its owner to be the admin key we just created.
To do so, we call the Create Wallet endpoint with the following body:
With this, we have a smart wallet created, and it can be controlled by the admin (agent owner), <admin-signer-address>
.
Create a Key for the Agent
As a next step, we want to create a separate key that will be held by our agent. This key will be able to send transactions on behalf of the smart wallet, but will have some restrictions attached.
By default, delegated signers cannot perform administrative operations on the wallet. This means they cannot:
- Add or remove other signers
- Change wallet configuration
- Upgrade the wallet implementation
- Transfer wallet ownership
Only the admin signer (wallet owner) has these privileges. This security measure ensures that delegated keys can only perform the specific operations they’ve been authorized for.
For this tutorial, we will generate the agent key following the same steps as the admin key:
Then we should store the private key somewhere safe that the agent can access. For example, as an environment variable in the server that the agent is running on.
Solana wallets support a maximum of 10 delegated signers due to blockchain transaction size limits. When planning your application, ensure you manage your signer slots efficiently. If you need to add a new signer when at capacity, consider removing an unused signer first.
And now we register the agent signer address as a Delegated Signer for the smart wallet, using the address locator returned when we created the wallet:
Solana transactions expire after 150 blocks (approximately 1-2 minutes) so you should try and send the approval before the transaction expires.
Approve Registration of Agent Signer with the Admin Key
The admin signer needs to approve the registration of the agent’s key as a delegated signer, by signing the transaction from the response - note the transaction’s status above is “awaiting-approval”.
We now need to submit that signed message using the Approve Transaction endpoint. In the route’s path include the wallet’s address and the transaction’s id, and pass the following body:
Once this transaction is successfully broadcasted on-chain, the agent key will have delegated access to the smart wallet 🎉
Check the Transaction Status
The delegated signer will become usable only after the transaction is confirmed on-chain. You can check the status by retrieving the signer details or by checking the transaction status using the transaction ID returned in the response.
To check the status of the transaction, use the Get Transaction endpoint. In the route’s path include the wallet’s address and the transaction’s id:
Send a Transaction from the Agent
Now that the agent’s key is approved, it can create transactions on the developer’s behalf but it won’t be allowed to modify the wallet configuration or approve new signers.
Here we are going to make a USDC transfer. The first step is to prepare the transaction:
Now we will use the transaction data generated and the Create Transaction API to submit a new transaction for the smart wallet with the following body:
Approve Transaction
The agent, via its delegated key, signs the transaction message to approve it. Then, it will be broadcasted on-chain.
Like before, the message to sign is “awaiting-approval” in the previous response.
To submit the transaction, use the Approve Transaction API to pass the signature in the body, the transactionId from Step 4 in the route’s path, and the wallet’s locator:
With this, our agent has already submitted its first transaction! 🕺
(Bonus) Check transactions for a wallet
The agent developer can easily fetch transactions executed by their wallet, as well as filter the responses based on signer to audit transactions by specific agents.
🎉 Advanced: Permissions System
Crossmint offers a comprehensive permissions system for delegated keys, allowing you to set precise boundaries on what actions delegated signers can perform.
EVM Smart Wallets
For EVM smart wallets, the permissions system is fully available and supports various restriction types including:
- Rate limiting (number of transactions per time period)
- Asset type restrictions (e.g., only NFTs, only specific tokens)
- Value limits (maximum transaction amounts)
- Contract address allowlists
Learn more about the complete set of options in the EVM Delegated Keys Permissions section.
Solana Smart Wallets
For Solana smart wallets, an advanced permissions system is coming soon. Currently, delegated signers have basic transaction capabilities but cannot perform administrative operations on the wallet.
Stay tuned for updates as we expand the permissions system for Solana wallets in upcoming releases. The upcoming permissions system will include token-specific restrictions, allowing you to limit which tokens a delegated signer can interact with.
Was this page helpful?