4. Delegate Management

Add, list, and remove delegates. Delegates are addresses that can propose transactions on behalf of Safe owners without being owners themselves.

What you'll learn

  • What delegates are and when to use them

  • Add a delegate for an owner using a signed API call

  • List all delegates for a Safe

  • Remove a delegate

Prerequisites

  • Node.js 18+

  • A Safe on a supported chain where you control an owner key

  • A private key for a Safe owner: testnet only

  • viem: used to create a wallet client for signing delegate API requests

Install dependencies:

npm install @safe-global/api-kit viem

Configuration

import SafeApiKitModule from "@safe-global/api-kit";
import { createWalletClient, http } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";

// ESM interop-safe constructor resolution (required in some runtimes)
const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;

const CHAIN_ID = 11155111n; // Sepolia
const TX_SERVICE_URL = `https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}`;
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com";
const SAFE_ADDRESS = "0xYourSafeAddress";
const OWNER_PRIVATE_KEY = "0xYourPrivateKey" as `0x${string}`; // Testnet only!

const apiKit = new SafeApiKit({
  chainId: CHAIN_ID,
  txServiceUrl: TX_SERVICE_URL,
});

This example uses sepolia for the viem wallet client. If you switch CHAIN_ID, use the matching viem chain object.

The TX_SERVICE_URL points at the Ledger-hosted Transaction Service. Delegate records stored here are visible in the Ledger Multisig UIarrow-up-right.

Supported chains: Ethereum (1), Optimism (10), BSC (56), Polygon (137), Base (8453), Arbitrum (42161), Sepolia (11155111).

Step-by-step

1

Set up the owner wallet client

Delegate management requires the owner to sign API requests (proving they authorize the delegate). Create a viem wallet client for the owner:

2

Add a delegate

Add a delegate address that can propose transactions on behalf of the owner. The label is a human-readable identifier stored with the delegate record.

REST API: POST /v2/delegates/

The request body includes the Safe address, delegate address, delegator (owner), label, and a signature proving the delegator authorized this action.

The delegate can now call proposeTransaction with senderAddress set to their own address.

3

List delegates

Query all delegates for the Safe:

REST API: GET /v2/delegates/?safe={address}

4

Remove a delegate

Remove a delegate when they should no longer be able to propose on the owner's behalf:

REST API: DELETE /v2/delegates/{delegateAddress}/

Requires a signature from the delegator (owner) proving they authorize the removal.

Verify the removal:

Key concepts

What are delegates?

A delegate is an address authorized to propose transactions to the Transaction Service on behalf of a Safe owner, without being an owner themselves.

Why use delegates?

  • Automation: A backend service or bot can propose transactions without holding an owner key

  • Separation of concerns: Proposers don't need signing authority; owners still approve and execute

  • No gas cost: Adding and removing delegates are off-chain API calls, not on-chain transactions

Important distinctions:

  • Delegates can propose but cannot sign or execute. Only owners can provide confirmations.

  • Delegate records are stored in the Transaction Service, not on-chain. They only affect who can call the proposal API.

  • Each delegate is associated with a specific delegator (owner). If the delegator is removed as an owner, their delegates lose proposal rights.

Tips and pitfalls

circle-info
  • No gas required. Delegate management is entirely off-chain. Adding, listing, and removing delegates are API calls to the Transaction Service. No on-chain transactions are involved.

  • Wallet client required for signing. The addSafeDelegate and removeSafeDelegate methods require a signer parameter. This is a viem WalletClient that signs the API request body, proving the owner authorized the action.

  • ESM import gotcha. In some ESM runtimes, using import SafeApiKit from "@safe-global/api-kit" directly can throw TypeError: SafeApiKit is not a constructor. Use the interop-safe constructor resolution shown in Configuration.

  • Delegates per owner. Each delegate is tied to a specific owner (delegator). If you want a delegate to act on behalf of multiple owners, add them separately for each owner.

  • Label is metadata only. The label field is stored alongside the delegate record for identification purposes. It has no functional effect.

Next steps

5. ERC20 Token Transferschevron-right

Last updated