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

  1. Enroll: The admin signer for the wallet sends a transaction to add a delegated signer to the wallet, optionally specifying a set of restrictions
  2. Transact: The delegated signer can now perform transactions
  3. 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

1

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.

Then, navigate to project Settings > General, and change the wallet type to Smart Wallets.

2

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, and wallets: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:

headers
{
    "X-API-KEY": "your_server_api_key",
    "Content-Type": "application/json",
}

Create a Delegated Key and Send a Transaction

1

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:

import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const privateKey = generatePrivateKey();
const adminSigner = privateKeyToAccount(privateKey);
console.log(adminSigner.address);
// > "0x234"

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:

{
   type: "evm-smart-wallet",
   config: {
        adminSigner: { 
            type: "evm-keypair", 
            address: "0x234" 
        }
   }
}

With this, we have a smart wallet created, and it can be controlled by the admin (agent owner), 0x234.

2

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:

import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

const agentKey = generatePrivateKey();
const agentSigner = privateKeyToAccount(agentKey);
console.log(agentSigner.address);
// > "0x569"

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:

POST https://staging.crossmint.com/api/2022-06-09/wallets/0xdeadbeef/signers

body: {
    signer: "evm-keypair:0x0x569
    chain: "base",
    expiresAt: 1735845180080  // timestamp in ms since Unix epoch

    // Additional signer permissions can be set as per ERC-7715
    // permissions: {
    //     type: "rate-limit",
    //     data: {
    //         count: 100,     // Allow 100 calls
    //         interval: 3600  // Within each 1-hour period (3600 seconds)
    //     }
    // },
}
3

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”.

const signature = adminSigner.signMessage({
    message: { raw: "0xme55ageTo5ign" },
});
console.log(signature);
// > 0xa22cb4650000000000000000000000000000000000000013

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:

{
    approvals: [
        {
            signer: "evm-keypair:0x234",
            signature: "0xa22cb4650000000000000000000000000000000000000013"
        }
    ]
}

And with this, the agent key now has delegated access to the smart wallet 🎉

4

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:

import { encodeFunctionData, parseUnits } from 'viem';

// USDC contract address on Base
const usdcAddress = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';

// Recipient address and amount to send (5 USDC)
const recipient = '0xRecipientAddress'; // Replace with the recipient's address
const amount = parseUnits('5', 6);      // 5 USDC (6 decimals)

// Encode the data for the transfer function
const data = encodeFunctionData({
    abi: [
        {
            "constant": false,
            "inputs": [
                { "name": "recipient", "type": "address" },
                { "name": "amount", "type": "uint256" }
            ],
            "name": "transfer",
            "outputs": [{ "name": "", "type": "bool" }],
            "type": "function"
        }
    ],
    functionName: 'transfer',
    args: [recipient, amount]
});

console.log('Transaction Data:', data);

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:

// Send 5 USDC to another agent
{
   params: {
      calls: [{
      		to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC address from above
      		data: "...", // data output from above
      		value: "0"
      }],
      // use the session key signer
      signer: "evm-keypair:0x569",
      chain: "base"
   }
}
5

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.

const signature = agentSigner.signMessage({
    message: { raw: "0xn3Wme55ageTo5ign" },
});
console.log(signature);
// > 0xa22cb4650000000000000000000000000000000000000013

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:

{
    approvals: [
        {
            signer: "evm-keypair:0x569",
            signature: "0xa22cb4650000000000000000000000000000000000000013"
        }
    ]
}

With this, our agent has already submitted its first transaction! 🕺

6

(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.

GET https://staging.crossmint.com/api/2022-06-09/wallets/0xdeadbeef/transactions

🎉 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.