# Ledger Enterprise Multisig API Overview

Welcome, developer. The Ledger Enterprise Multisig API provides programmatic access to the industry's most secure, transparent, and user-friendly platform for on-chain digital asset management.

Our platform is built to solve the critical security gaps in the multisig market, combining the robust, open-source infrastructure of Safe with Ledger's unparalleled Clear Signing and enterprise-grade security layer.

This API is designed for crypto native organisations who need to:

* Automate treasury operations.
* Build custom management workflows.
* Integrate Ledger-secured multisig operations directly into their applications.

#### Our Architecture: Secure & Familiar

To accelerate adoption and ensure reliability, Ledger Enterprise Multisig is built upon the battle-tested, open-source Safe backend.

This means that if you are familiar with the Safe API, you will find our endpoints and data structures virtually identical. This ensures a seamless and rapid integration, allowing you to leverage your existing knowledge while immediately benefiting from the Ledger security ecosystem. All API services are hosted under the Ledger Multisig domain and integrated into our secure infrastructure.


# Supported Networks

## Supported Networks

Ledger Enterprise Multisig is designed to provide a unified, secure management layer across the most active digital asset ecosystems. Currently, our platform fully supports the major EVM-compatible chains, ensuring you can secure your treasury and administer smart contracts where the majority of DeFi activity occurs.

#### Currently Supported Networks

You can deploy, import, and manage Safe Accounts on the following Mainnet networks:

| Supported Network          | Chain ID  |
| -------------------------- | --------- |
| Ethereum Mainnet           | 1n        |
| Base                       | 8453n     |
| Arbitrum                   | 42161n    |
| Polygon                    | 137n      |
| Optimism                   | 10n       |
| Ethereum Sepolia (testnet) | 11155111n |

#### Upcoming Network Support

As part of our vision to be the go-to multisig solution for EVMs, we are adding new EVM networks every month.


# Guides

Practical, end-to-end tutorials for integrating with the Ledger Multisig Transaction Service

We've put together a number of practical, step by step guides that will help you integrate the Ledger Multisig workflow into your Multisig setup, allowing more complex workflows that support signing by both a Virtual Machine and a Ledger Device for ultimate security.&#x20;

### Recommended order

| Step | Tutorial                                                                                            | Focus                                          | Requires private key |
| ---- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------- | -------------------- |
| 1    | [Querying Safe Data](/guides/api-guides/1.-querying-safe-data)                                      | Read-only Safe data, history, delegates, nonce | No                   |
| 2    | [Transaction Lifecycle](/guides/api-guides/2.-transaction-lifecycle-including-off-chain-signatures) | Create, sign, propose, execute, verify         | Yes                  |
| 3    | [Batch Transactions](/guides/api-guides/3.-batch-transactions)                                      | MultiSend batching and atomic execution        | Yes                  |
| 4    | [Delegate Management](/guides/api-guides/4.-delegate-management)                                    | Add/list/remove delegates                      | Yes                  |
| 5    | [ERC-20 Token Transfers](/guides/api-guides/5.-erc20-token-transfers)                               | ABI encode + propose token calls               | Yes                  |
| 6    | [Multi-Signature Flow](/guides/api-guides/6.-multi-signature-flow)                                  | 2-of-2 owner/threshold lifecycle               | Yes                  |

### Shared configuration

All tutorials use the same Transaction Service base URL pattern:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

This points to the Ledger-hosted Transaction Service backend (not the public Safe service).

### Before you start

* Use testnet keys only when setting up the service to avoid overspending and production issues.
* Fund the Safe for gas before execution tutorials.
* Expect indexer lag on the Ledger Multisig platform (typically 10-60s) after on-chain execution.
* Use the interop-safe SDK import pattern shown in each tutorial for ESM runtimes.

### Troubleshooting

If you hit constructor errors, stale threshold data, or reverted executions:

* Troubleshooting


# Transactions with off-chain signatures

This guide demonstrates how to programmatically interact with the Transaction Service API to create, sign, and execute transactions within the Ledger Multisig environment.

While Ledger Multisig provides a seamless "magical" UI for these operations, developers can leverage the underlying infrastructure to build custom workflows, automated treasury operations, or complex smart contract interactions.

> Security Note: Ledger Multisig adds an experience and security layer on top of Safe's infrastructure. The code below utilizes the standard protocol kits, ensuring full compatibility with your existing Safe setup while preparing you for the Ledger ecosystem.

### Prerequisites

* [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm#using-a-node-version-manager-to-install-nodejs-and-npm) (when using the TypeScript/JS Kit).
* [Python](https://www.python.org/downloads/) >= 3.9 (when using `safe-eth-py`).
* A configured Safe (Ledger Multisig Account) with a threshold of 2 (requiring at least two signatures).

### Steps

{% stepper %}
{% step %}

### Install dependencies

To begin, install the necessary kits to interact with the protocol and the API.

{% tabs %}
{% tab title="TypeScript" %}

```bash
yarn add @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit
```

{% endtab %}

{% tab title="Python" %}

```bash
pip install safe-eth-py web3 hexbytes
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Imports

Import the necessary modules to handle the transaction logic and type definitions.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKit from '@safe-global/api-kit'
import Safe from '@safe-global/protocol-kit'
import {
  MetaTransactionData,
  OperationType
} from '@safe-global/types-kit'

```

{% endtab %}

{% tab title="Python" %}

```python
from safe_eth.eth import EthereumClient, EthereumNetwork
from safe_eth.safe.api.transaction_service_api import TransactionServiceApi
from safe_eth.safe import Safe
from hexbytes import HexBytes

```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Create a multisig transaction

Initialize the Protocol Kit with the credentials of the first signer (Owner A). In a production Ledger Multisig environment, this ensures the transaction is properly formatted before being proposed to the network.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Initialize the Protocol Kit with Owner A
const protocolKitOwnerA = await Safe.init({
  provider: config.RPC_URL,
  signer: config.OWNER_A_PRIVATE_KEY,
  safeAddress: config.SAFE_ADDRESS
})

// Create the transaction payload
const safeTransactionData: MetaTransactionData = {
  to: config.TO,
  value: config.VALUE,
  data: '0x',
  operation: OperationType.Call
}

const safeTransaction = await protocolKitOwnerA.createTransaction({
  transactions: [safeTransactionData]
})
```

{% endtab %}

{% tab title="Python" %}

```python
ethereum_client = EthereumClient(config.get("RPC_URL"))

# Instantiate a Safe
safe = Safe(config.get("SAFE_ADDRESS"), ethereum_client)

# Create the transaction payload
safe_tx = safe.build_multisig_tx(
    config.get("TO"),
    config.get("VALUE"),
    HexBytes(""))
```

{% endtab %}

{% tab title="Curl" %}

```bash
curl -X 'POST' \
'https://multisig.ledger.com/tx-service/sep/api/v1/safes/0xc62C5cbB964ffffffffff82f78A4d30713174b2E/multisig-transactions/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{
    "safe": "0xc62C5cbB964459F3C984682f78A4d3ffffffffff",
    "to": "0x795D6C88B4Ea3CCffffffffffCa8a11Bc0496228",
    "value": 2000000000000000,
    "data": null,
    "operation": 0,
    "gasToken": "0x0000000000000000000000000000000000000000",
    "safeTxGas": 0,
    "baseGas": 0,
    "gasPrice": 0,
    "refundReceiver": "0x0000000000000000000000000000000000000000",
    "nonce": 15,
    "contractTransactionHash": "0x56b2931d1053b6afffffffffff3ba29b5c2baafdf1a588850da72a62674941b6",
    "sender": "0xAA86E576c084aCFa56fc4D0E17967ffffffffff8",
    "signature": "0x6a2b57023af16241511619ea95f7cd03d00aa6b79d1ca80e21a0b89cd2c38ffffffffff9b738ffbd680c4d717b9b0c9eae568f3edebc40a0c004700bffffffffff"
}'
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Generate Initial Signature

Before the transaction can be shared with other owners via the Transaction Service, it must be signed by the initiator.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Sign the transaction with Owner A
const safeTxHash = await protocolKitOwnerA.getTransactionHash(safeTransaction)
const signatureOwnerA = await protocolKitOwnerA.signHash(safeTxHash)
```

{% endtab %}

{% tab title="Python" %}

```python
# Sign the transaction with Owner A
safe_tx.sign(config.get("OWNER_A_PRIVATE_KEY"))
```

{% endtab %}

{% tab title="Curl" %}
We skip this step because the transaction we created in the Transaction Service using Curl already has the signature of the transaction creator. Check the [Create a Safe transaction](https://docs.safe.global/core-api/transaction-service-guides/transactions#create-a-safe-transaction) step.
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Send the transaction to the service <a href="#send-the-transaction-to-the-service" id="send-the-transaction-to-the-service"></a>

Once signed, the transaction is sent to the Transaction Service. This makes the transaction visible in the Pending Transactions section of the Ledger Multisig interface, allowing other owners to review and sign it.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Initialize the API Kit
// How to get an Api key => http://docs.safe.global/core-api/how-to-use-api-keys
const apiKit = new SafeApiKit({
  chainId: 11155111n
})

// Send the transaction to the Transaction Service with the signature from Owner A
await apiKit.proposeTransaction({
  safeAddress: config.SAFE_ADDRESS,
  safeTransactionData: safeTransaction.data,
  safeTxHash,
  senderAddress: config.OWNER_A_ADDRESS,
  senderSignature: signatureOwnerA.data
})
```

{% endtab %}

{% tab title="Python" %}

```python
# Instantiate the Transaction Service API
transaction_service_api = TransactionServiceApi(
    network=EthereumNetwork.SEPOLIA,
    ethereum_client=ethereum_client,
)

# Send the transaction to the Transaction Service with the signature from Owner A
transaction_service_api.post_transaction(safe_tx)
```

{% endtab %}

{% tab title="Curl" %}
We skip this step because the transaction we created using Curl is already in the Transaction Service. Check the [Create a Safe transaction](https://docs.safe.global/core-api/transaction-service-guides/transactions#create-a-safe-transaction) step.
{% endtab %}
{% endtabs %}

{% endstep %}

{% step %}

#### Gather Multi-Signature Approvals

For a threshold of 2, a second signer (Owner B) must retrieve the pending transaction and append their signature. In a real-world scenario, Owner B could also be a Ledger Stax or Flex user signing via the interface.

**Get the pending transaction:**

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const signedTransaction = await apiKit.getTransaction(safeTxHash)
```

{% endtab %}

{% tab title="Python" %}

```python
(safe_tx_from_tx_service, _) = transaction_service_api.get_safe_transaction(
    safe_tx_hash)
```

{% endtab %}

{% tab title="Curl" %}

```bash
curl -X 'GET' \
'https://multisig.ledger.com/tx-service/sep/api/v1/multisig-transactions/0xe4ceea4ffffffffff0c9ce0af82780ffffffffffd3096836fff2528cb90d156/' \
-H 'accept: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}
{% endtabs %}

**Add the missing signature:**

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Initialize the Protocol Kit with Owner B
const protocolKitOwnerB = await Safe.init({
  provider: config.RPC_URL,
  signer: config.OWNER_B_PRIVATE_KEY,
  safeAddress: config.SAFE_ADDRESS
})

// Sign the transaction with Owner B
const signatureOwnerB = await protocolKitOwnerB.signHash(safeTxHash)

// Send the transaction to the Transaction Service with the signature from Owner B
await apiKit.confirmTransaction(
  safeTxHash,
  signatureOwnerB.data
)
```

{% endtab %}

{% tab title="Python" %}

```python
# Sign the transaction with Owner B
owner_b_signature = safe_tx_from_tx_service.sign(
    config.get("OWNER_B_PRIVATE_KEY"))

# Send the transaction to the Transaction Service with the signature from Owner B
transaction_service_api.post_signatures(
    safe_tx_from_tx_service.safe_tx_hash,
    owner_b_signature)
```

{% endtab %}

{% tab title="Curl" %}

```bash
curl -X 'POST' \
'https://multisig.ledger.com/tx-service/sep/api/v1/multisig-transactions/0x56b2931ffffffffff303bbaadf3ba29b5c2baafdf1a5ffffffffff62674941b6/confirmations/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{
    "signature": "0x8c41aaa029db2942a1574c3ec9442b80764e953f21f994edb641d33ffffffffffa55ffffffffffefcaea5b3360bd4c789803fe289b2ad467fc9b3bedb479dad20"
}'
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Execute the Transaction

Once the threshold is met, the transaction is ready for execution. This broadcasts the fully signed transaction to the blockchain.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const transactionResponse =
  await protocolKitOwnerA.executeTransaction(signedTransaction)
```

{% endtab %}

{% tab title="Python" %}

```python
result = safe_tx_from_tx_service.execute(config.get("OWNER_A_PRIVATE_KEY"))
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Verify Execution

You can query the history to verify the transaction has been successfully indexed. This will reflect in the "Latest Transactions" view on your Ledger Multisig Dashboard.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const transactions = await apiKit.getMultisigTransactions(config.SAFE_ADDRESS)

if (transactions.results.length > 0) {
  console.log('Last executed transaction', transactions.results[0])
}
```

{% endtab %}

{% tab title="Python" %}

```python
transactions = transaction_service_api.get_transactions(
    config.get("SAFE_ADDRESS"))

last_executed_tx = next(
    (x for x in transactions if x.get('isExecuted')),
    None)
```

{% endtab %}

{% tab title="Curl" %}

```shellscript
curl -X 'GET' \
'https://multisig.ledger.com/tx-service/sep/api/v1/safes/0xc62C5cbB96ffffffffffff2f78A4d3071317ffff/multisig-transactions/?executed=true&limit=1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}


# API Guides

* [1. Querying Safe Data](/guides/api-guides/1.-querying-safe-data)
* [2. Transaction Lifecycle (including off-chain signatures)](/guides/api-guides/2.-transaction-lifecycle-including-off-chain-signatures)
* [3. Batch Transactions](/guides/api-guides/3.-batch-transactions)
* [4. Delegate Management](/guides/api-guides/4.-delegate-management)
* [5. ERC20 Token Transfers](/guides/api-guides/5.-erc20-token-transfers)
* [6. Multi Signature Flow](/guides/api-guides/6.-multi-signature-flow)
* [7. Troubleshooting](/guides/api-guides/7.-troubleshooting)


# 1. Querying Safe Data

Read Safe configuration, balances, transaction history, delegates, and nonce from the Ledger Enterprise Multisig API. No private keys required.

### Shared configuration

All tutorials use the same Transaction Service base URL pattern:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

### What you'll learn

* Initialize the Safe API Kit against the Ledger Enterprise Multisig Transaction Service.
* Query Safe info, creation data, balances, and transaction history.
* List delegates and retrieve the next available nonce.
* Understand which endpoints the SDK wraps and which require direct `fetch.`

### Prerequisites

* **Node.js 18+** and a package manager (npm, pnpm, or yarn)
* **A Safe deployed on a supported chain,** you can use any existing Safe
* No private key or API key is needed for read-only queries.
* Install the SDK:

```bash
npm install @safe-global/api-kit @safe-global/types-kit
```

### Configuration

All examples in this tutorial use the Ledger Enterprise Multisig Transaction Service. This is the Ledger-hosted backend, transactions and Safes indexed here appear in the [Ledger Enterprise Multisig UI](https://app.multisig.ledger.com). It is **not** the public Safe Transaction Service.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKitModule from "@safe-global/api-kit";

// 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 SAFE_ADDRESS = "0xYourSafeAddress";

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

{% endtab %}

{% tab title="Python" %}

```python
import requests

CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
SAFE_ADDRESS = "0xYourSafeAddress"

session = requests.Session()
session.headers.update({"accept": "application/json"})
```

{% endtab %}

{% tab title="curl" %}

```bash
export CHAIN_ID=11155111  # Sepolia
export TX_SERVICE_URL="https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}"
export SAFE_ADDRESS="0xYourSafeAddress"
```

{% endtab %}
{% endtabs %}

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

Replace the `CHAIN_ID` with the numeric chain ID for your target network. The URL pattern is always:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

### Steps

{% stepper %}
{% step %}

### Get Safe info

Retrieve the on-chain configuration of a Safe: owners, threshold, nonce, version, modules, and guard.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const safeInfo = await apiKit.getSafeInfo(SAFE_ADDRESS);

console.log("Address:", safeInfo.address);
console.log("Threshold:", safeInfo.threshold);
console.log("Owners:", safeInfo.owners);
console.log("Nonce:", safeInfo.nonce);
console.log("Version:", safeInfo.version);
console.log("Modules:", safeInfo.modules);
console.log("Fallback handler:", safeInfo.fallbackHandler);
console.log("Guard:", safeInfo.guard);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/")
resp.raise_for_status()
safe_info = resp.json()

print("Address:", safe_info["address"])
print("Threshold:", safe_info["threshold"])
print("Owners:", safe_info["owners"])
print("Nonce:", safe_info["nonce"])
print("Version:", safe_info.get("version"))
print("Modules:", safe_info.get("modules", []))
print("Fallback handler:", safe_info.get("fallbackHandler"))
print("Guard:", safe_info.get("guard"))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v1/safes/{address}/`\
> Example: `GET https://app.multisig.ledger.com/api/safe-transaction-service/11155111/v1/safes/0xYourSafe/`
> {% endstep %}

{% step %}

### Get Safe creation info

Find out when and how a Safe was deployed, the creator address, factory, and deployment transaction hash.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const creation = await apiKit.getSafeCreationInfo(SAFE_ADDRESS);

console.log("Created:", creation.created);
console.log("Creator:", creation.creator);
console.log("Tx hash:", creation.transactionHash);
console.log("Factory:", creation.factoryAddress);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/creation/")
resp.raise_for_status()
creation = resp.json()

print("Created:", creation["created"])
print("Creator:", creation["creator"])
print("Tx hash:", creation["transactionHash"])
print("Factory:", creation["factoryAddress"])
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/creation/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v1/safes/{address}/creation/`
> {% endstep %}

{% step %}

### Get balances

Query native and token balances held by the Safe.

> **Important:** The API Kit does **not** expose a `getSafeBalances` method. Use a direct `fetch` call against the v2 balances endpoint.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const response = await fetch(`${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/balances/`);
if (!response.ok) {
  throw new Error(`Failed to fetch balances: HTTP ${response.status}`);
}

const data = (await response.json()) as {
  count: number;
  results: Array<{
    tokenAddress: string | null;
    token: { symbol: string; decimals: number } | null;
    balance: string;
  }>;
};

function formatUnits(raw: string, decimals: number) {
  const value = BigInt(raw);
  const base = 10n ** BigInt(decimals);
  const whole = value / base;
  const fraction = (value % base).toString().padStart(decimals, "0").replace(/0+$/, "");
  return fraction ? `${whole}.${fraction}` : `${whole}`;
}

for (const entry of data.results) {
  const symbol = entry.token?.symbol ?? "ETH";
  const decimals = entry.token?.decimals ?? 18;
  const human = formatUnits(entry.balance, decimals);
  console.log(`${symbol}: ${human}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/balances/")
resp.raise_for_status()
data = resp.json()

def format_units(raw: str, decimals: int) -> str:
    value = int(raw)
    base = 10 ** decimals
    whole = value // base
    frac = value % base
    if frac == 0:
        return str(whole)
    frac_str = str(frac).rjust(decimals, "0").rstrip("0")
    return f"{whole}.{frac_str}"

for entry in data["results"]:
    symbol = (entry.get("token") or {}).get("symbol") or "ETH"
    decimals = (entry.get("token") or {}).get("decimals") or 18
    print(f"{symbol}: {format_units(entry['balance'], int(decimals))}")
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/balances/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/safes/{address}/balances/`
> {% endstep %}

{% step %}

### Get multisig transactions

List all multisig transactions (both executed and pending) for the Safe, ordered by nonce.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const txs = await apiKit.getMultisigTransactions(SAFE_ADDRESS);

console.log("Total:", txs.count);

for (const tx of txs.results.slice(0, 5)) {
  console.log("safeTxHash:", tx.safeTxHash);
  console.log("to:", tx.to);
  console.log("value:", tx.value);
  console.log("nonce:", tx.nonce);
  console.log("executed:", tx.isExecuted);
  console.log("successful:", tx.isSuccessful);
  console.log(`confirmations: ${tx.confirmations?.length ?? 0} / ${tx.confirmationsRequired}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/multisig-transactions/?limit=5")
resp.raise_for_status()
txs = resp.json()

print("Total:", txs["count"])
for tx in txs["results"][:5]:
    print("safeTxHash:", tx["safeTxHash"])
    print("to:", tx["to"])
    print("value:", tx["value"])
    print("nonce:", tx["nonce"])
    print("executed:", tx["isExecuted"])
    print("successful:", tx.get("isSuccessful"))
    confirmations = (tx.get("confirmations") or [])
    print(f"confirmations: {len(confirmations)} / {tx['confirmationsRequired']}")
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/multisig-transactions/?limit=5" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/safes/{address}/multisig-transactions/`
> {% endstep %}

{% step %}

### Get pending transactions

Filter to only transactions that have been proposed but not yet executed.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const pending = await apiKit.getPendingTransactions(SAFE_ADDRESS);

console.log("Pending count:", pending.count);

for (const tx of pending.results) {
  console.log("safeTxHash:", tx.safeTxHash);
  console.log("nonce:", tx.nonce);
  console.log(`confirmations: ${tx.confirmations?.length ?? 0} / ${tx.confirmationsRequired}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
# REST equivalent: filter multisig txs to only not executed.
resp = session.get(
    f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/multisig-transactions/?executed=false"
)
resp.raise_for_status()
pending = resp.json()

print("Pending count:", pending["count"])
for tx in pending["results"]:
    confirmations = (tx.get("confirmations") or [])
    print("safeTxHash:", tx["safeTxHash"])
    print("nonce:", tx["nonce"])
    print(f"confirmations: {len(confirmations)} / {tx['confirmationsRequired']}")
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/multisig-transactions/?executed=false" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Get all transactions (including incoming transfers)

Returns every transaction type: multisig, module, and incoming transfers (ETH and ERC-20 received by the Safe).

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const all = await apiKit.getAllTransactions(SAFE_ADDRESS, { limit: 5 });

console.log("Total:", all.count);

for (const tx of all.results) {
  const record = tx as Record<string, unknown>;
  console.log("type:", record.txType);
  console.log("hash:", record.transactionHash ?? record.txHash);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/all-transactions/?limit=5")
resp.raise_for_status()
all_txs = resp.json()

print("Total:", all_txs["count"])
for tx in all_txs["results"]:
    # Different types return different hash fields.
    tx_type = tx.get("txType")
    tx_hash = tx.get("transactionHash") or tx.get("txHash")
    print("type:", tx_type)
    print("hash:", tx_hash)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/all-transactions/?limit=5" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/safes/{address}/all-transactions/`
> {% endstep %}

{% step %}

### Get delegates

List all delegates authorized to propose transactions on behalf of Safe owners.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const delegates = await apiKit.getSafeDelegates({
  safeAddress: SAFE_ADDRESS,
});

console.log("Delegate count:", delegates.count);

for (const d of delegates.results) {
  console.log("delegate:", d.delegate);
  console.log("delegator:", d.delegator);
  console.log("label:", d.label);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/delegates/?safe={SAFE_ADDRESS}")
resp.raise_for_status()
delegates = resp.json()

print("Delegate count:", delegates["count"])
for d in delegates["results"]:
    print("delegate:", d["delegate"])
    print("delegator:", d["delegator"])
    print("label:", d.get("label"))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/delegates/?safe=${SAFE_ADDRESS}" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/delegates/?safe={address}`
> {% endstep %}

{% step %}

### Get next nonce

Returns the next nonce to use when creating a new Safe transaction. This accounts for both executed and pending (queued) transactions.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const nextNonce = await apiKit.getNextNonce(SAFE_ADDRESS);

console.log("Next nonce:", nextNonce);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/next-nonce/")
resp.raise_for_status()
next_nonce = resp.json()

print("Next nonce:", next_nonce)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/next-nonce/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

### Key concepts

> **The Safe Transaction Service** is an off-chain backend that indexes on-chain Safe events and stores proposed (not-yet-executed) transactions. The Ledger Enterprise Multisig Transaction Service is Ledger's hosted instance of this service. When you query it, you get:
>
> * **On-chain state** (owners, threshold, nonce) synced from the blockchain
> * **Off-chain proposals** (pending transactions with their collected signatures)
> * **Full transaction history** including incoming transfers
>
> The API Kit is a TypeScript wrapper around the Transaction Service REST API. Most endpoints are covered, but some (like balances) require direct HTTP calls.

### Tips and pitfalls

* **`getSafeBalances` does not exist.** The API Kit (v2.5.7) does not wrap the balances endpoint. Use a direct `fetch()` call to `/v2/safes/{address}/balances/` as shown in step 3.
* **No API key required.** The Ledger Multisig Transaction Service does not enforce API keys for read operations. No rate-limiting headers are needed.
* **ESM import gotcha.** In some ESM runtimes, `import SafeApiKit from "@safe-global/api-kit"` throws `TypeError: SafeApiKit is not a constructor`. Use the interop-safe constructor resolution shown in Configuration.
* **Indexing lag.** After an on-chain transaction executes, the Transaction Service may take 10–60 seconds to index the new state. If you query immediately after execution and see stale data, wait and retry.
* **Paginated responses.** Methods like `getMultisigTransactions` return paginated results. Use the `limit` and `offset` parameters (or `next`/`previous` URLs in the response) to page through large result sets.

### Next steps

* Tutorial index
* **Transaction Lifecycle:** Create, sign, propose, and execute a Safe transaction
* **Verified runnable source:** `playground/src/phase1-read.ts`
* Troubleshooting
* [Safes API Reference](https://ledger-4.gitbook.io/ledger-multisig/reference/safes)
* [Transactions API Reference](https://ledger-4.gitbook.io/ledger-multisig/reference/transactions)
* [Ledger Multisig Overview](https://ledger-4.gitbook.io/ledger-multisig)


# 2. Transaction Lifecycle (including off-chain signatures)

Create, sign, propose, execute, and verify a Safe transaction end-to-end using the Ledger Enterprise Multisig API.

## What you'll learn

* Initialize both the Protocol Kit (transaction creation/signing) and the API Kit (proposal/indexing)
* Build and sign an ETH transfer as a Safe transaction
* Propose the signed transaction to the Ledger Multisig Transaction Service
* Execute the transaction on-chain once the signature threshold is met
* Verify execution status through the API

## Prerequisites

* **Node.js 18+**
* **A Safe on Sepolia** (or another supported chain) where you control at least one owner key
* **A private key** for a Safe owner: use a **testnet-only** key
* **Sepolia ETH** in the Safe for gas and the transfer amount
* **viem**: used for receipt verification

Install the SDK:

```bash
npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit viem
```

## Configuration

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKitModule from "@safe-global/api-kit";
import SafeModule from "@safe-global/protocol-kit";
import { OperationType } from "@safe-global/types-kit";
import type { MetaTransactionData } from "@safe-global/types-kit";
import { createPublicClient, http } from "viem";
import { sepolia } from "viem/chains";

// ESM interop-safe constructor resolution (required in some runtimes)
const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;
const Safe =
  typeof SafeModule === "function"
    ? SafeModule
    : (SafeModule as unknown as { default: typeof SafeModule }).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 = "your-private-key"; // Testnet only!
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"
SAFE_ADDRESS = "0xYourSafeAddress"
OWNER_PRIVATE_KEY = "your-private-key"  # Testnet only!

session = requests.Session()
session.headers.update({"accept": "application/json"})
```

{% endtab %}

{% tab title="curl" %}

```bash
export CHAIN_ID=11155111  # Sepolia
export TX_SERVICE_URL="https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}"
export RPC_URL="https://ethereum-sepolia-rpc.publicnode.com"
export SAFE_ADDRESS="0xYourSafeAddress"
export OWNER_PRIVATE_KEY="your-private-key"  # Testnet only!
```

{% endtab %}
{% endtabs %}

The `TX_SERVICE_URL` points at the **Ledger-hosted** Transaction Service. Transactions proposed here appear in the [Ledger Enterprise Multisig UI](https://app.multisig.ledger.com/).

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

## Step-by-step

{% stepper %}
{% step %}

### Initialize the Protocol Kit and API Kit

The Protocol Kit connects to the blockchain and handles transaction creation and signing. The API Kit talks to the Transaction Service for proposal and querying.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const protocolKit = await Safe.init({
  provider: RPC_URL,
  signer: OWNER_PRIVATE_KEY,
  safeAddress: SAFE_ADDRESS,
});

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

const signerAddress = await protocolKit.getSafeProvider().getSignerAddress();

// Verify the signer is an owner
const safeInfo = await apiKit.getSafeInfo(SAFE_ADDRESS);
const isOwner = safeInfo.owners
  .map((o) => o.toLowerCase())
  .includes(signerAddress!.toLowerCase());

if (!isOwner) {
  throw new Error(`${signerAddress} is not an owner of this Safe`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
from eth_account import Account

# Derive the public address from the private key
signer_address = Account.from_key(OWNER_PRIVATE_KEY).address

# Verify the signer is an owner (via Transaction Service)
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/")
resp.raise_for_status()
safe_info = resp.json()

owners = [o.lower() for o in safe_info["owners"]]
if signer_address.lower() not in owners:
    raise ValueError(f"{signer_address} is not an owner of this Safe")
```

{% endtab %}

{% tab title="curl" %}

```bash
# Fetch Safe config (owners, threshold, nonce, ...)
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> REST API: `GET /v1/safes/{address}/` - used internally by `getSafeInfo` to verify ownership.
> {% endstep %}

{% step %}

### Create the transaction

Define the transaction parameters and create a Safe transaction object. For a simple ETH transfer, set `data` to `"0x"` and `operation` to `Call`.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const txData: MetaTransactionData = {
  to: "0xRecipientAddress",
  value: "100000000000000", // 0.0001 ETH in wei
  data: "0x",
  operation: OperationType.Call,
};

const safeTransaction = await protocolKit.createTransaction({
  transactions: [txData],
});
```

{% endtab %}

{% tab title="Python" %}

```python
# For Python transaction building/execution, use safe-eth-py.
from safe_eth.eth import EthereumClient
from safe_eth.safe import Safe
from hexbytes import HexBytes

ethereum_client = EthereumClient(RPC_URL)
safe = Safe(SAFE_ADDRESS, ethereum_client)

safe_tx = safe.build_multisig_tx(
    to="0xRecipientAddress",
    value=100000000000000,  # 0.0001 ETH in wei
    data=HexBytes("0x"),
    operation=0,  # CALL
)
```

{% endtab %}

{% tab title="curl" %}

```bash
# Transaction creation happens client-side.
# With curl you can only submit the *final* payload to the service
# once you have a computed safeTxHash and an owner signature.
```

{% endtab %}
{% endtabs %}

The Protocol Kit automatically sets the nonce, `safeTxGas`, `baseGas`, `gasPrice`, `gasToken`, and `refundReceiver` fields based on the current Safe state.
{% endstep %}

{% step %}

### Sign the transaction

Compute the Safe transaction hash and sign it with the owner's private key. This produces an off-chain (EIP-712) signature with no gas is spent.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const safeTxHash = await protocolKit.getTransactionHash(safeTransaction);
const signature = await protocolKit.signHash(safeTxHash);
```

{% endtab %}

{% tab title="Python" %}

```python
# safe-eth-py can sign the tx after building it.
signature = safe_tx.sign(OWNER_PRIVATE_KEY)
safe_tx_hash = safe_tx.safe_tx_hash.hex()
```

{% endtab %}

{% tab title="curl" %}

```bash
# Signing is cryptographic and must happen client-side (not via the service).
# Use a local signer, HSM, or wallet SDK to sign the safeTxHash.
```

{% endtab %}
{% endtabs %}

The `safeTxHash` is the unique identifier for this transaction within the Safe. It is **not** an on-chain transaction hash.
{% endstep %}

{% step %}

### Propose the transaction

Submit the signed transaction to the Transaction Service. This stores it off-chain and makes it visible to other owners in the Ledger Multisig UI.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: safeTransaction.data,
  safeTxHash,
  senderAddress: signerAddress!,
  senderSignature: signature.data,
});
```

{% endtab %}

{% tab title="Python" %}

```python
# Propose by calling the REST endpoint directly.
# The service expects the full tx fields plus:
# - contractTransactionHash (safeTxHash)
# - sender
# - signature
payload = {
    "safe": SAFE_ADDRESS,
    "to": safe_tx.to,
    "value": safe_tx.value,
    "data": safe_tx.data.hex(),
    "operation": safe_tx.operation,
    "gasToken": safe_tx.gas_token,
    "safeTxGas": safe_tx.safe_tx_gas,
    "baseGas": safe_tx.base_gas,
    "gasPrice": safe_tx.gas_price,
    "refundReceiver": safe_tx.refund_receiver,
    "nonce": safe_tx.nonce,
    "contractTransactionHash": safe_tx.safe_tx_hash.hex(),
    "sender": signer_address,
    "signature": signature.hex() if hasattr(signature, "hex") else str(signature),
}

resp = session.post(
    f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/multisig-transactions/",
    json=payload,
)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X POST \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/multisig-transactions/" \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d '{
    "safe": "'"${SAFE_ADDRESS}"'",
    "to": "0xRecipientAddress",
    "value": "100000000000000",
    "data": "0x",
    "operation": 0,
    "gasToken": "0x0000000000000000000000000000000000000000",
    "safeTxGas": "0",
    "baseGas": "0",
    "gasPrice": "0",
    "refundReceiver": "0x0000000000000000000000000000000000000000",
    "nonce": "0",
    "contractTransactionHash": "0xYourSafeTxHash",
    "sender": "0xOwnerAddress",
    "signature": "0xOwnerSignature"
  }'
```

{% endtab %}
{% endtabs %}

> REST API: `POST /v1/safes/{address}/multisig-transactions/` - the request body includes the full transaction data, the `safeTxHash`, the sender address, and the signature.

After proposing, the transaction is visible in the Ledger Enterprise Multisig UI and other owners can sign the transaction.
{% endstep %}

{% step %}

### Check confirmations and execute

Retrieve the pending transaction, check if enough signatures have been collected to meet the threshold, and execute on-chain if ready.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const pendingTx = await apiKit.getTransaction(safeTxHash);
const confirmationCount = pendingTx.confirmations?.length ?? 0;

if (confirmationCount >= pendingTx.confirmationsRequired) {
  const executionResult = await protocolKit.executeTransaction(pendingTx);
  console.log("Transaction hash:", executionResult.hash);
} else {
  console.log(
    `Need ${pendingTx.confirmationsRequired - confirmationCount} more signature(s)`
  );
}
```

{% endtab %}

{% tab title="Python" %}

```python
# Fetch the pending tx and inspect confirmations.
resp = session.get(f"{TX_SERVICE_URL}/v1/multisig-transactions/{safe_tx_hash}/")
resp.raise_for_status()
pending = resp.json()

confirmation_count = len(pending.get("confirmations") or [])
required = pending["confirmationsRequired"]

if confirmation_count < required:
    print(f"Need {required - confirmation_count} more signature(s)")
else:
    print("Threshold met. Execute on-chain using a Safe SDK (recommended).")
```

{% endtab %}

{% tab title="curl" %}

```bash
# Retrieve a single transaction by safeTxHash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/multisig-transactions/0xYourSafeTxHash/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> REST API: `GET /v1/multisig-transactions/{safeTxHash}/` - retrieves a single transaction by its Safe transaction hash, including all collected confirmations.

If the threshold is not yet met, other owners can confirm using:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
await apiKit.confirmTransaction(safeTxHash, theirSignature.data);
```

{% endtab %}

{% tab title="Python" %}

```python
from eth_account import Account
from eth_account.messages import encode_defunct

# "theirSignature" is a signature over the safeTxHash bytes32.
# One common approach is an EIP-191 personal_sign over the hash.
msg = encode_defunct(hexstr=safe_tx_hash)
their_signature = Account.sign_message(msg, private_key="their-private-key").signature.hex()

resp = session.post(
    f"{TX_SERVICE_URL}/v1/multisig-transactions/{safe_tx_hash}/confirmations/",
    json={"signature": their_signature},
)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X POST \
  "${TX_SERVICE_URL}/v1/multisig-transactions/0xYourSafeTxHash/confirmations/" \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d '{ "signature": "0xOwnerSignature" }'
```

{% endtab %}
{% endtabs %}

> REST API: `POST /v1/multisig-transactions/{safeTxHash}/confirmations/`
> {% endstep %}

{% step %}

### Verify execution

After execution, query the Transaction Service to confirm the transaction was indexed as executed and successful.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Wait for the Transaction Service to index the execution
await new Promise((resolve) => setTimeout(resolve, 10_000));

// Re-fetch the exact tx by safeTxHash (safer than "latest tx")
const updated = await apiKit.getTransaction(safeTxHash);
console.log("Executed:", updated.isExecuted);
console.log("Successful:", updated.isSuccessful);
console.log("On-chain hash:", updated.transactionHash);
```

{% endtab %}

{% tab title="Python" %}

```python
import time

time.sleep(10)

resp = session.get(f"{TX_SERVICE_URL}/v1/multisig-transactions/{safe_tx_hash}/")
resp.raise_for_status()
updated = resp.json()

print("Executed:", updated.get("isExecuted"))
print("Successful:", updated.get("isSuccessful"))
print("On-chain hash:", updated.get("transactionHash"))
```

{% endtab %}

{% tab title="curl" %}

```bash
sleep 10
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/multisig-transactions/0xYourSafeTxHash/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## Key concepts

> Off-chain signatures, on-chain execution. Safe transactions use a two-phase model:
>
> * Propose: The transaction data and an owner's signature are submitted to the Transaction Service (off-chain, no gas).
> * Execute: Once enough signatures meet the threshold, any account can submit the transaction on-chain (costs gas).
>
> This means a 3-of-5 Safe only pays gas once - when the final executor submits all collected signatures in a single on-chain call.
>
> For the full guide, see [Transactions with Off-chain Signatures](https://ledger-4.gitbook.io/ledger-multisig/guides/transactions-with-off-chain-signatures).

## Tips and pitfalls

* **Indexing lag.** After `executeTransaction` returns, the Transaction Service may take 10–60 seconds to index the receipt. If you query immediately, `isExecuted` may still be `false` and `isSuccessful` may be `null`. The on-chain transaction is confirmed. The indexer catches up within a minute or two.
* **`executeTransaction` can return a hash for a reverted tx.** The Protocol Kit returns a transaction hash even if the on-chain transaction reverts. Always check the receipt status independently.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const publicClient = createPublicClient({
  chain: sepolia,
  transport: http(RPC_URL),
});

const receipt = await publicClient.waitForTransactionReceipt({
  hash: executionResult.hash as `0x${string}`,
  timeout: 120_000,
});

if (receipt.status === "reverted") {
  throw new Error("Transaction reverted on-chain");
}
```

{% endtab %}

{% tab title="Python" %}

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider(RPC_URL))
receipt = w3.eth.wait_for_transaction_receipt(
    execution_tx_hash,
    timeout=120,
)

# web3.py uses status: 1 (success) / 0 (revert)
if receipt["status"] == 0:
    raise RuntimeError("Transaction reverted on-chain")
```

{% endtab %}

{% tab title="curl" %}

```bash
# Most RPCs support eth_getTransactionReceipt.
curl -sS -X POST "${RPC_URL}" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0xOnChainTxHash"]}'
```

{% endtab %}
{% endtabs %}

* **Signer must be an owner.** The `proposeTransaction` call will fail if `senderAddress` is not a current owner (or delegate) of the Safe. Verify ownership before proposing.
* **Nonce management.** The Protocol Kit automatically picks the next nonce. If you need to queue transactions in a specific order, pass `options: { nonce }` to `createTransaction`.

## Next steps

{% content-ref url="/pages/2f500d7500a478df6d16aa68d444bf529ab9cfac" %}
[3. Batch Transactions](/guides/api-guides/3.-batch-transactions)
{% endcontent-ref %}


# 3. Batch Transactions

Bundle multiple operations into a single atomic Safe transaction using the MultiSend contract.

## What you'll learn

* Create a batch transaction containing multiple ETH transfers
* Understand how the Protocol Kit uses the MultiSend contract under the hood
* Sign, propose, and execute a batch as a single on-chain transaction

## Prerequisites

* **Node.js 18+**
* **A Safe on Sepolia** where you control an owner key
* **A private key** for a Safe owner: testnet only
* **Sepolia ETH** in the Safe to cover the batch transfers and gas
* **viem**: used for receipt verification

Install the SDK:

```bash
npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit viem
```

## Configuration

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKitModule from "@safe-global/api-kit";
import SafeModule from "@safe-global/protocol-kit";
import { OperationType } from "@safe-global/types-kit";
import type { MetaTransactionData } from "@safe-global/types-kit";
import { createPublicClient, http } from "viem";
import { sepolia } from "viem/chains";

// ESM interop-safe constructor resolution (required in some runtimes)
const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;
const Safe =
  typeof SafeModule === "function"
    ? SafeModule
    : (SafeModule as unknown as { default: typeof SafeModule }).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 = "your-private-key"; // Testnet only!
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"
SAFE_ADDRESS = "0xYourSafeAddress"
OWNER_PRIVATE_KEY = "your-private-key"  # Testnet only!

session = requests.Session()
session.headers.update({"accept": "application/json"})
```

{% endtab %}

{% tab title="curl" %}

```bash
export CHAIN_ID=11155111  # Sepolia
export TX_SERVICE_URL="https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}"
export RPC_URL="https://ethereum-sepolia-rpc.publicnode.com"
export SAFE_ADDRESS="0xYourSafeAddress"
export OWNER_PRIVATE_KEY="your-private-key"  # Testnet only!
```

{% endtab %}
{% endtabs %}

The `TX_SERVICE_URL` points at the **Ledger-hosted** Transaction Service. Transactions proposed here appear in the [Ledger Multisig UI](https://app.multisig.ledger.com/).

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

## Step-by-step

{% stepper %}
{% step %}

### Initialize

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const protocolKit = await Safe.init({
  provider: RPC_URL,
  signer: OWNER_PRIVATE_KEY,
  safeAddress: SAFE_ADDRESS,
});

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

const signerAddress = await protocolKit.getSafeProvider().getSignerAddress();
```

{% endtab %}

{% tab title="Python" %}

```python
from eth_account import Account

# Derive the public address from the private key
signer_address = Account.from_key(OWNER_PRIVATE_KEY).address

# Optional: verify the signer is an owner (via Transaction Service)
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/")
resp.raise_for_status()
safe_info = resp.json()

owners = [o.lower() for o in safe_info["owners"]]
if signer_address.lower() not in owners:
    raise ValueError(f"{signer_address} is not an owner of this Safe")
```

{% endtab %}

{% tab title="curl" %}

```bash
# Fetch Safe config (owners, threshold, nonce, ...)
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Define multiple operations

Create an array of `MetaTransactionData` objects. Each one represents a distinct operation. Here we batch two ETH transfers:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const transactions: MetaTransactionData[] = [
  {
    to: "0xRecipient1",
    value: "100000000000000", // 0.0001 ETH
    data: "0x",
    operation: OperationType.Call,
  },
  {
    to: "0xRecipient2",
    value: "200000000000000", // 0.0002 ETH
    data: "0x",
    operation: OperationType.Call,
  },
];
```

{% endtab %}

{% tab title="Python" %}

```python
transactions = [
    {
        "to": "0xRecipient1",
        "value": 100000000000000,  # 0.0001 ETH (wei)
        "data": "0x",
        "operation": 0,  # CALL
    },
    {
        "to": "0xRecipient2",
        "value": 200000000000000,  # 0.0002 ETH (wei)
        "data": "0x",
        "operation": 0,  # CALL
    },
]
```

{% endtab %}

{% tab title="curl" %}

```bash
# Batch composition happens client-side.
# With curl, you can only submit the final proposed tx payload
# once you have the MultiSend call data, safeTxHash, and a signature.
```

{% endtab %}
{% endtabs %}

You can mix operation types in a batch: ETH transfers, contract calls, ERC-20 approvals, and more. Each operation has its own `to`, `value`, `data`, and `operation` fields.
{% endstep %}

{% step %}

### Create the batch transaction

Pass the array to `createTransaction`. When the array contains more than one item, the Protocol Kit automatically wraps them in a call to the **MultiSend** contract.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const safeTransaction = await protocolKit.createTransaction({ transactions });
```

{% endtab %}

{% tab title="Python" %}

```python
# You still need to build a *single* Safe tx that calls MultiSend.
# There is no official Python Protocol Kit equivalent.
#
# Practical option:
# - Build the batch with TypeScript `@safe-global/protocol-kit`
# - Then propose it from Python via REST (see the next step)
```

{% endtab %}

{% tab title="curl" %}

```bash
# Same constraint as Python:
# you must compute the MultiSend calldata + safeTxHash client-side.
```

{% endtab %}
{% endtabs %}

Under the hood, the Protocol Kit:

{% stepper %}
{% step %}
ABI-encodes each operation into the MultiSend format.
{% endstep %}

{% step %}
Sets `to` to the MultiSend contract address (chain-specific, deployed by Safe).
{% endstep %}

{% step %}
Sets `operation` to `DelegateCall` (required for MultiSend).
{% endstep %}

{% step %}
Packs all operations into the `data` field.
{% endstep %}
{% endstepper %}

You don't need to interact with the MultiSend contract directly. The SDK handles it.
{% endstep %}

{% step %}

### Sign the batch

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const safeTxHash = await protocolKit.getTransactionHash(safeTransaction);
const signature = await protocolKit.signHash(safeTxHash);
```

{% endtab %}

{% tab title="Python" %}

```python
# Signing is always client-side.
# Once you have the safeTxHash, sign it with any local signer.
# Then submit the signature to the service (next step).
```

{% endtab %}

{% tab title="curl" %}

```bash
# Signing is cryptographic and must happen client-side (not via the service).
# Use a local signer, HSM, or wallet SDK to sign the safeTxHash.
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Propose to the Transaction Service

{% tabs %}
{% tab title="TypeScript" %}

```typescript
await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: safeTransaction.data,
  safeTxHash,
  senderAddress: signerAddress!,
  senderSignature: signature.data,
});
```

{% endtab %}

{% tab title="Python" %}

```python
# The service expects a regular Safe multisig tx proposal.
# For MultiSend batches:
# - "to" is the MultiSend contract address
# - "operation" is 1 (DELEGATE_CALL)
# - "data" is the MultiSend-encoded payload
payload = {
    "safe": SAFE_ADDRESS,
    "to": "0xMultiSendAddress",
    "value": "0",
    "data": "0xMultiSendCalldata",
    "operation": 1,
    "gasToken": "0x0000000000000000000000000000000000000000",
    "safeTxGas": "0",
    "baseGas": "0",
    "gasPrice": "0",
    "refundReceiver": "0x0000000000000000000000000000000000000000",
    "nonce": "0",
    "contractTransactionHash": "0xYourSafeTxHash",
    "sender": "0xOwnerAddress",
    "signature": "0xOwnerSignature",
}

resp = session.post(
    f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/multisig-transactions/",
    json=payload,
)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X POST \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/multisig-transactions/" \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d '{
    "safe": "'"${SAFE_ADDRESS}"'",
    "to": "0xMultiSendAddress",
    "value": "0",
    "data": "0xMultiSendCalldata",
    "operation": 1,
    "gasToken": "0x0000000000000000000000000000000000000000",
    "safeTxGas": "0",
    "baseGas": "0",
    "gasPrice": "0",
    "refundReceiver": "0x0000000000000000000000000000000000000000",
    "nonce": "0",
    "contractTransactionHash": "0xYourSafeTxHash",
    "sender": "0xOwnerAddress",
    "signature": "0xOwnerSignature"
  }'
```

{% endtab %}
{% endtabs %}

> REST API: `POST /v1/safes/{address}/multisig-transactions/`

The batch transaction appears as a single entry in the Ledger Multisig UI.
{% endstep %}

{% step %}

### Execute

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const publicClient = createPublicClient({
  chain: sepolia,
  transport: http(RPC_URL),
});

const pendingTx = await apiKit.getTransaction(safeTxHash);
const confirmations = pendingTx.confirmations?.length ?? 0;

if (confirmations >= pendingTx.confirmationsRequired) {
  const result = await protocolKit.executeTransaction(pendingTx);
  const receipt = await publicClient.waitForTransactionReceipt({
    hash: result.hash as `0x${string}`,
    timeout: 120_000,
  });
  if (receipt.status === "reverted") {
    throw new Error("Batch reverted on-chain");
  }
  console.log("Batch executed. Tx hash:", result.hash);
}
```

{% endtab %}

{% tab title="Python" %}

```python
# Execution is an on-chain call to the Safe contract.
# If you don't have a Python SDK that can:
# - rebuild the Safe tx (to=MultiSend, operation=1, data=calldata)
# - collect/pack signatures
# prefer executing via the TypeScript Protocol Kit.
```

{% endtab %}

{% tab title="curl" %}

```bash
# Execution cannot be done via the Transaction Service API.
# You must submit an on-chain tx to the Safe contract (eth_sendRawTransaction).
```

{% endtab %}
{% endtabs %}

Both transfers happen atomically in a single on-chain transaction. Either all succeed or all revert.
{% endstep %}
{% endstepper %}

## Key concepts

What is MultiSend?

MultiSend is a contract deployed by the Safe team on every supported chain. It lets you batch multiple operations into a single transaction by:

* Encoding each operation (target, value, data, operation type) into a packed byte array
* Executing all of them via `delegatecall` from the Safe

Because it uses `delegatecall`, the individual operations execute in the context of the Safe. `msg.sender` in each sub-call is the Safe itself.

Why batch?

* **Gas efficiency:** One on-chain transaction instead of N
* **Atomicity:** All operations succeed or all revert
* **Single approval flow:** Owners sign once for the entire batch
* **Lower nonce usage:** The batch consumes only one Safe nonce

## Tips and pitfalls

* **Indexing lag.** After the batch executes on-chain, the Transaction Service may take 10–60 seconds to index the result. If you query immediately, `isExecuted` might still be `false`.
* **Always verify the receipt.** `executeTransaction` can return a hash even when the transaction eventually reverts. Wait for the receipt and check `receipt.status`.
* **DelegateCall is set automatically.** When you pass multiple transactions to `createTransaction`, the SDK sets the operation to `DelegateCall` for the outer MultiSend call. Don't override this manually.
* **Mixing ETH and contract calls.** You can freely mix native ETH transfers (data `"0x"`) and contract interactions (encoded calldata) in the same batch. See [ERC-20 Token Transfers](broken://pages/3ff5a6496eb4a944e65156123199c49fa67a95bf) for encoding contract calls.
* **Order matters.** Operations execute sequentially in the order you define them. If operation B depends on the result of operation A (e.g., approve then transferFrom), put A first.
* **Error in one reverts all.** If any operation in the batch fails, the entire MultiSend transaction reverts. Test each operation individually before batching.

## Next steps

{% content-ref url="/pages/494a02d578d9836eadeb5a1f7574c3176756e8bd" %}
[4. Delegate Management](/guides/api-guides/4.-delegate-management)
{% endcontent-ref %}


# 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:

```bash
npm install @safe-global/api-kit viem
```

## Configuration

{% tabs %}
{% tab title="TypeScript" %}

```typescript
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,
});
```

{% endtab %}

{% tab title="Python" %}

```python
import time
import requests
from eth_account import Account
from eth_account.messages import encode_structured_data

CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
SAFE_ADDRESS = "0xYourSafeAddress"
OWNER_PRIVATE_KEY = "your-private-key"  # Testnet only!

session = requests.Session()
session.headers.update({"accept": "application/json"})

owner_address = Account.from_key(OWNER_PRIVATE_KEY).address

def totp() -> int:
    # T0=0, Tx=3600. Time-based window in hours.
    return int(time.time()) // 3600

def sign_delegate_request(delegate_address: str) -> str:
    typed_data = {
        "types": {
            "EIP712Domain": [
                {"name": "name", "type": "string"},
                {"name": "version", "type": "string"},
                {"name": "chainId", "type": "uint256"},
            ],
            "Delegate": [
                {"name": "delegateAddress", "type": "address"},
                {"name": "totp", "type": "uint256"},
            ],
        },
        "primaryType": "Delegate",
        "domain": {
            "name": "Safe Transaction Service",
            "version": "1.0",
            "chainId": CHAIN_ID,
        },
        "message": {
            "delegateAddress": delegate_address,
            "totp": totp(),
        },
    }

    signable = encode_structured_data(primitive=typed_data)
    sig = Account.sign_message(signable, private_key=OWNER_PRIVATE_KEY).signature.hex()
    return sig
```

{% endtab %}

{% tab title="curl" %}

```bash
export CHAIN_ID=11155111  # Sepolia
export TX_SERVICE_URL="https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}"
export SAFE_ADDRESS="0xYourSafeAddress"
export OWNER_ADDRESS="0xOwnerAddress"
export DELEGATE_ADDRESS="0xDelegateAddress"

# You must compute:
# - totp = floor(unix_time_seconds / 3600)
# - signature = EIP-712 signature over (delegateAddress, totp)
export TOTP="0"
export SIGNATURE="0xOwnerSignature"
```

{% endtab %}
{% endtabs %}

> 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 UI](https://app.multisig.ledger.com/).

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

## Step-by-step

{% stepper %}
{% step %}

### 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:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const ownerAccount = privateKeyToAccount(OWNER_PRIVATE_KEY);
const ownerWalletClient = createWalletClient({
  account: ownerAccount,
  chain: sepolia,
  transport: http(RPC_URL),
});
const ownerAddress = ownerAccount.address;
```

{% endtab %}

{% tab title="Python" %}

```python
# In Python you typically sign delegate API requests locally (EIP-712).
# The helper `sign_delegate_request(...)` in Configuration does that.
#
# owner_address is already derived from OWNER_PRIVATE_KEY in Configuration.
```

{% endtab %}

{% tab title="curl" %}

```bash
# Signing happens client-side.
# Use any EIP-712 capable signer to produce SIGNATURE.
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

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

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const delegateAddress = "0xDelegateAddress";

await apiKit.addSafeDelegate({
  safeAddress: SAFE_ADDRESS,
  delegateAddress,
  delegatorAddress: ownerAddress,
  signer: ownerWalletClient,
  label: "Backend Service",
});
```

{% endtab %}

{% tab title="Python" %}

```python
delegate_address = "0xDelegateAddress"
label = "Backend Service"

signature = sign_delegate_request(delegate_address)

payload = {
    "safe": SAFE_ADDRESS,
    "delegate": delegate_address,
    "delegator": owner_address,
    "label": label,
    "signature": signature,
}

resp = session.post(f"{TX_SERVICE_URL}/v2/delegates/", json=payload)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X POST \
  "${TX_SERVICE_URL}/v2/delegates/" \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d '{
    "safe": "'"${SAFE_ADDRESS}"'",
    "delegate": "'"${DELEGATE_ADDRESS}"'",
    "delegator": "'"${OWNER_ADDRESS}"'",
    "label": "Backend Service",
    "signature": "'"${SIGNATURE}"'"
  }'
```

{% endtab %}
{% endtabs %}

> **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.
{% endstep %}

{% step %}

### List delegates

Query all delegates for the Safe:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const delegates = await apiKit.getSafeDelegates({
  safeAddress: SAFE_ADDRESS,
});

for (const d of delegates.results) {
  console.log("delegate:", d.delegate);
  console.log("delegator:", d.delegator);
  console.log("label:", d.label);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/delegates/?safe={SAFE_ADDRESS}")
resp.raise_for_status()
delegates = resp.json()

for d in delegates["results"]:
    print("delegate:", d["delegate"])
    print("delegator:", d["delegator"])
    print("label:", d.get("label"))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/delegates/?safe=${SAFE_ADDRESS}" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/delegates/?safe={address}`
> {% endstep %}

{% step %}

### Remove a delegate

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

{% tabs %}
{% tab title="TypeScript" %}

```typescript
await apiKit.removeSafeDelegate({
  delegateAddress,
  delegatorAddress: ownerAddress,
  signer: ownerWalletClient,
});
```

{% endtab %}

{% tab title="Python" %}

```python
# The DELETE request is also authorized by an EIP-712 signature.
# The typed data is the same as for adding a delegate (delegateAddress + totp).
signature = sign_delegate_request(delegate_address)

resp = session.delete(
    f"{TX_SERVICE_URL}/v2/delegates/{delegate_address}/",
    params={"delegator": owner_address, "signature": signature},
)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X DELETE \
  "${TX_SERVICE_URL}/v2/delegates/${DELEGATE_ADDRESS}/?delegator=${OWNER_ADDRESS}&signature=${SIGNATURE}" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `DELETE /v2/delegates/{delegateAddress}/`
>
> Requires a signature from the delegator (owner) proving they authorize the removal.

Verify the removal:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const remaining = await apiKit.getSafeDelegates({
  safeAddress: SAFE_ADDRESS,
});
console.log("Delegates remaining:", remaining.count);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/delegates/?safe={SAFE_ADDRESS}")
resp.raise_for_status()
remaining = resp.json()
print("Delegates remaining:", remaining["count"])
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/delegates/?safe=${SAFE_ADDRESS}" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## 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

{% hint style="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.
  {% endhint %}

## Next steps

{% content-ref url="/pages/265f124e505d916cc3b14406fd71284a403032ce" %}
[5. ERC20 Token Transfers](/guides/api-guides/5.-erc20-token-transfers)
{% endcontent-ref %}


# 5. ERC20 Token Transfers

Encode ERC-20 function calls and propose them as Safe transactions through the Ledger Multisig API.

## What you'll learn

* Encode ERC-20 `transfer()` and `approve()` calldata using viem
* Create a Safe transaction that calls a token contract (value = 0, data = calldata)
* Sign and propose the token transaction to the Transaction Service

## 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
* **An ERC-20 token balance** in the Safe (for actual execution)
* **viem**: used for ABI encoding

Install dependencies:

```bash
npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit viem
```

## Configuration

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKitModule from "@safe-global/api-kit";
import SafeModule from "@safe-global/protocol-kit";
import { OperationType } from "@safe-global/types-kit";
import { encodeFunctionData, parseAbi } from "viem";
import { createPublicClient, http } from "viem";
import { sepolia } from "viem/chains";

const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;
const Safe =
  typeof SafeModule === "function"
    ? SafeModule
    : (SafeModule as unknown as { default: typeof SafeModule }).default;

const CHAIN_ID = 11155111n;
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 = "your-private-key";
```

{% endtab %}

{% tab title="Python" %}

```python
import os
import requests
from web3 import Web3
from eth_abi import encode as abi_encode

# Chain / service config
CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"

# Safe config
SAFE_ADDRESS = "0xYourSafeAddress"
OWNER_PRIVATE_KEY = "your-private-key"  # testnet only

session = requests.Session()
session.headers.update({"accept": "application/json"})

w3 = Web3(Web3.HTTPProvider(RPC_URL))
```

{% endtab %}

{% tab title="curl" %}

```ini
# These examples show the HTTP payloads you’d send with curl.
# Any signing / hashing must be done client-side.

CHAIN_ID=11155111
TX_SERVICE_URL=https://app.multisig.ledger.com/api/safe-transaction-service/11155111
SAFE_ADDRESS=0xYourSafeAddress
OWNER_ADDRESS=0xOwnerAddress
TOKEN_ADDRESS=0xTokenContractAddress
```

{% endtab %}
{% endtabs %}

The `TX_SERVICE_URL` points at the **Ledger-hosted** Transaction Service. Transactions proposed here appear in the [Ledger Multisig UI](https://app.multisig.ledger.com/).

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

## Step-by-step

{% stepper %}
{% step %}

### Define the ERC-20 ABI

Parse the standard ERC-20 function signatures you'll use. viem's `parseAbi` creates a typed ABI from human-readable signatures:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const erc20Abi = parseAbi([
  "function transfer(address to, uint256 amount)",
  "function approve(address spender, uint256 amount)",
  "function balanceOf(address owner) view returns (uint256)",
]);
```

{% endtab %}

{% tab title="Python" %}

```python
# We’ll encode calls using:
# - 4-byte selector = keccak256("transfer(address,uint256)")[:4]
# - ABI-encoded args (address, uint256)

TRANSFER_SIG = "transfer(address,uint256)"
APPROVE_SIG = "approve(address,uint256)"
BALANCE_OF_SIG = "balanceOf(address)"
```

{% endtab %}

{% tab title="curl" %}

```http
# ABI encoding is always client-side.
# curl is only used to call:
# - the Transaction Service REST API
# - your JSON-RPC endpoint (optional)
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Encode transfer calldata

Encode the `transfer(address, uint256)` function call. This produces the raw calldata bytes that the Safe will send to the token contract.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const TOKEN_ADDRESS = "0xTokenContractAddress";
const RECIPIENT = "0xRecipientAddress";
const AMOUNT = 1000n * 10n ** 18n; // 1000 tokens (assuming 18 decimals)

const transferCalldata = encodeFunctionData({
  abi: erc20Abi,
  functionName: "transfer",
  args: [RECIPIENT as `0x${string}`, AMOUNT],
});

// transferCalldata starts with the 4-byte selector for transfer(address,uint256):
// 0xa9059cbb...
```

{% endtab %}

{% tab title="Python" %}

```python
from eth_utils import keccak, to_bytes

TOKEN_ADDRESS = "0xTokenContractAddress"
RECIPIENT = "0xRecipientAddress"
AMOUNT = 1000 * 10**18  # 1000 tokens (assuming 18 decimals)

selector = keccak(text=TRANSFER_SIG)[:4]
args = abi_encode(["address", "uint256"], [RECIPIENT, AMOUNT])

transfer_calldata = Web3.to_hex(selector + args)
# starts with 0xa9059cbb...
```

{% endtab %}

{% tab title="curl" %}

```http
# You’ll need `transfer_calldata` computed client-side.
# It becomes the `data` field in the Transaction Service proposal payload.
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Encode approve calldata (optional)

The same pattern works for `approve`. It's commonly used before interacting with DeFi protocols:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const SPENDER = "0xDeFiProtocolAddress";

const approveCalldata = encodeFunctionData({
  abi: erc20Abi,
  functionName: "approve",
  args: [SPENDER as `0x${string}`, AMOUNT],
});
```

{% endtab %}

{% tab title="Python" %}

```python
SPENDER = "0xDeFiProtocolAddress"

selector = keccak(text=APPROVE_SIG)[:4]
args = abi_encode(["address", "uint256"], [SPENDER, AMOUNT])

approve_calldata = Web3.to_hex(selector + args)
```

{% endtab %}

{% tab title="curl" %}

```http
# Same rule as transfer:
# compute `approve_calldata` client-side, then propose it via REST.
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Create a Safe transaction with the encoded data

The key difference from a plain ETH transfer: set `value` to `"0"` (no ETH is sent) and `data` to the encoded calldata. The `to` field is the **token contract address**, not the recipient.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const protocolKit = await Safe.init({
  provider: RPC_URL,
  signer: OWNER_PRIVATE_KEY,
  safeAddress: SAFE_ADDRESS,
});

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

const safeTransaction = await protocolKit.createTransaction({
  transactions: [
    {
      to: TOKEN_ADDRESS,           // Token contract, NOT the recipient
      value: "0",                  // No ETH sent - value is in calldata
      data: transferCalldata,      // Encoded transfer(to, amount)
      operation: OperationType.Call,
    },
  ],
});
```

{% endtab %}

{% tab title="Python" %}

```python
# Python doesn’t have an official Protocol Kit.
# Practical flow:
# 1) Build (to, value, data, operation) as below.
# 2) Compute safeTxHash + signature using a Safe SDK / signer.
# 3) Propose via Transaction Service REST API.

TOKEN_ADDRESS = "0xTokenContractAddress"

tx_fields = {
    "to": TOKEN_ADDRESS,
    "value": "0",
    "data": transfer_calldata,
    "operation": 0,  # CALL
}
```

{% endtab %}

{% tab title="curl" %}

```http
# Proposed txs are stored off-chain in the Transaction Service.
# You submit the full tx payload + safeTxHash + an owner/delegate signature.
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Sign and propose

The signing and proposal flow is identical to any other Safe transaction:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const signerAddress = await protocolKit.getSafeProvider().getSignerAddress();
const safeTxHash = await protocolKit.getTransactionHash(safeTransaction);
const signature = await protocolKit.signHash(safeTxHash);

await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: safeTransaction.data,
  safeTxHash,
  senderAddress: signerAddress!,
  senderSignature: signature.data,
});
```

{% endtab %}

{% tab title="Python" %}

```python
# You must compute these client-side:
# - safe_tx_hash: bytes32 hash for the Safe tx
# - signature: owner signature over safe_tx_hash
safe_tx_hash = "0xYourSafeTxHash"
signature = "0xOwnerSignature"
owner_address = "0xOwnerAddress"

payload = {
    "safe": SAFE_ADDRESS,
    "to": tx_fields["to"],
    "value": tx_fields["value"],
    "data": tx_fields["data"],
    "operation": tx_fields["operation"],
    "gasToken": "0x0000000000000000000000000000000000000000",
    "safeTxGas": "0",
    "baseGas": "0",
    "gasPrice": "0",
    "refundReceiver": "0x0000000000000000000000000000000000000000",
    "nonce": "0",
    "contractTransactionHash": safe_tx_hash,
    "sender": owner_address,
    "signature": signature,
}

resp = session.post(
    f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/multisig-transactions/",
    json=payload,
)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```http
POST /v1/safes/{SAFE_ADDRESS}/multisig-transactions/
Accept: application/json
Content-Type: application/json

{
  "safe": "0xYourSafeAddress",
  "to": "0xTokenContractAddress",
  "value": "0",
  "data": "0xTransferCalldata",
  "operation": 0,
  "gasToken": "0x0000000000000000000000000000000000000000",
  "safeTxGas": "0",
  "baseGas": "0",
  "gasPrice": "0",
  "refundReceiver": "0x0000000000000000000000000000000000000000",
  "nonce": "0",
  "contractTransactionHash": "0xYourSafeTxHash",
  "sender": "0xOwnerAddress",
  "signature": "0xOwnerSignature"
}
```

{% endtab %}
{% endtabs %}

> **REST API:** `POST /v1/safes/{address}/multisig-transactions/`
> {% endstep %}

{% step %}

### Execute (when threshold is met)

Once enough owner signatures are collected:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const publicClient = createPublicClient({
  chain: sepolia,
  transport: http(RPC_URL),
});

const pendingTx = await apiKit.getTransaction(safeTxHash);

if ((pendingTx.confirmations?.length ?? 0) >= pendingTx.confirmationsRequired) {
  const result = await protocolKit.executeTransaction(pendingTx);
  const receipt = await publicClient.waitForTransactionReceipt({
    hash: result.hash as `0x${string}`,
    timeout: 120_000,
  });
  if (receipt.status === "reverted") {
    throw new Error("ERC-20 execution reverted on-chain");
  }
  console.log("Token transfer executed. Tx hash:", result.hash);
}
```

{% endtab %}

{% tab title="Python" %}

```python
# Execution is an on-chain call to the Safe contract.
# Recommended: execute with a Safe SDK that:
# - rebuilds the Safe tx
# - packs signatures in address-sorted order
#
# You can still check whether the Transaction Service collected enough signatures:
resp = session.get(f"{TX_SERVICE_URL}/v1/multisig-transactions/{safe_tx_hash}/")
resp.raise_for_status()
pending = resp.json()

if len(pending.get("confirmations") or []) < pending["confirmationsRequired"]:
    raise RuntimeError("Not enough confirmations to execute yet")
```

{% endtab %}

{% tab title="curl" %}

```http
GET /v1/multisig-transactions/{safeTxHash}/
Accept: application/json
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## Key concepts

**How ERC-20 calls work through a Safe:**

When a Safe sends tokens, it doesn't transfer ETH. It calls a function on the token contract. The Safe is the `msg.sender`, and the token contract's internal accounting moves the balance.

Safe → token.transfer(recipient, amount) ↓ Token contract: balances\[Safe] -= amount balances\[recipient] += amount

In Safe transaction terms:

| Field       | ETH Transfer      | ERC-20 Transfer                |
| ----------- | ----------------- | ------------------------------ |
| `to`        | Recipient address | Token contract address         |
| `value`     | Amount in wei     | `"0"`                          |
| `data`      | `"0x"`            | Encoded `transfer(to, amount)` |
| `operation` | `Call`            | `Call`                         |

## Common ERC-20 patterns

### Transfer tokens

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Safe sends tokens to a recipient
{ to: tokenAddress, value: "0", data: encodeTransfer(recipient, amount) }
```

{% endtab %}

{% tab title="Python" %}

```python
# Same structure as the REST proposal fields:
tx_fields = {"to": token_address, "value": "0", "data": transfer_calldata, "operation": 0}
```

{% endtab %}

{% tab title="curl" %}

```http
# Include these fields in the Transaction Service proposal payload:
# to=token_address, value="0", data=transfer_calldata, operation=0
```

{% endtab %}
{% endtabs %}

### Approve a spender

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Safe approves a DeFi protocol to spend tokens
{ to: tokenAddress, value: "0", data: encodeApprove(spender, amount) }
```

{% endtab %}

{% tab title="Python" %}

```python
tx_fields = {"to": token_address, "value": "0", "data": approve_calldata, "operation": 0}
```

{% endtab %}

{% tab title="curl" %}

```http
# Same proposal shape as transfer, but data=approve_calldata
```

{% endtab %}
{% endtabs %}

### Batch: approve + swap

Combine approval and a DeFi interaction in a single Safe transaction using [batch transactions](broken://pages/fc84917ad6adffbe187b956d3c5a4499c87463fc):

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const transactions = [
  { to: tokenAddress, value: "0", data: approveCalldata, operation: OperationType.Call },
  { to: routerAddress, value: "0", data: swapCalldata, operation: OperationType.Call },
];

const safeTx = await protocolKit.createTransaction({ transactions });
```

{% endtab %}

{% tab title="Python" %}

```python
# Batching is normally built with the TypeScript Protocol Kit (MultiSend wrapping).
# In Python you’d typically propose the final wrapped tx once you have:
# - to=multi_send_address
# - operation=1 (DELEGATECALL)
# - data=multi_send_calldata
```

{% endtab %}

{% tab title="curl" %}

```http
# Same constraints as Python:
# the MultiSend calldata must be computed client-side.
```

{% endtab %}
{% endtabs %}

## Tips and pitfalls

* **`to` is the token contract, not the recipient.** A common mistake is setting `to` to the address you want tokens sent to. The `to` field must be the token contract address. The actual recipient is encoded in the `data` field.
* **`value` must be `"0"` for ERC-20 calls.** Unless you're also sending ETH alongside the token call (rare), set value to zero.
* **Check the token's decimals.** Not all tokens use 18 decimals. USDC uses 6, WBTC uses 8. Encoding the wrong amount is a common source of errors:

Example:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// USDC: 6 decimals
const usdcAmount = 1000n * 10n ** 6n; // 1000 USDC
```

{% endtab %}

{% tab title="Python" %}

```python
# USDC: 6 decimals
usdc_amount = 1000 * 10**6  # 1000 USDC
```

{% endtab %}

{% tab title="curl" %}

```http
# Decimals affect the uint256 you encode in calldata.
# For USDC, multiply by 10^6 (not 10^18).
```

{% endtab %}
{% endtabs %}

* **Use viem for encoding.** The `encodeFunctionData` function from viem handles ABI encoding correctly and provides TypeScript type safety. Don't manually construct calldata.
* **Any contract call follows this pattern.** ERC-20 is just one example. The same approach works for any smart contract interaction: encode the function call as `data`, set `to` to the contract address, and set `value` to the ETH amount needed (usually `"0"`).
* **Tested playground script is propose-only.** The verified example script uses a dummy token address and intentionally does not execute on-chain. For execution, use a real ERC-20 token contract address on your target chain.

## Next steps

{% content-ref url="/pages/ad004d924a03999a4d86299df8aad3597bf05f42" %}
[6. Multi Signature Flow](/guides/api-guides/6.-multi-signature-flow)
{% endcontent-ref %}


# 6. Multi Signature Flow

Walk through the full lifecycle of a multi-owner Safe: add an owner, change the threshold, propose with one signer, confirm with another, and execute once the threshold is met.

## What you'll learn

* Add and remove owners from a Safe programmatically
* Change the signing threshold
* Propose a transaction signed by one owner, then confirm with a second
* Execute a transaction that requires multiple signatures
* Handle Transaction Service indexing lag in multi-step flows

## Prerequisites

* **Node.js 18+**
* **A Safe on Sepolia** where you control an owner key (starting as 1-of-1)
* **A private key** for the existing Safe owner: testnet only
* **Sepolia ETH** in the Safe for gas (multiple on-chain transactions)
* **viem**: used for address derivation and transaction receipts

Install dependencies:

```bash
npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit viem
```

## Configuration

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKitModule from "@safe-global/api-kit";
import SafeModule from "@safe-global/protocol-kit";
import { OperationType } from "@safe-global/types-kit";
import { createPublicClient, http } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { randomBytes } from "node:crypto";

const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;
const Safe =
  typeof SafeModule === "function"
    ? SafeModule
    : (SafeModule as unknown as { default: typeof SafeModule }).default;

const CHAIN_ID = 11155111n;
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_A_PRIVATE_KEY = "owner-a-private-key";
```

{% endtab %}

{% tab title="Python" %}

```python
import time
import requests
from web3 import Web3

CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"

SAFE_ADDRESS = "0xYourSafeAddress"
OWNER_A_PRIVATE_KEY = "owner-a-private-key"  # testnet only

session = requests.Session()
session.headers.update({"accept": "application/json"})

w3 = Web3(Web3.HTTPProvider(RPC_URL))
```

{% endtab %}

{% tab title="curl" %}

```ini
# Use these values in your REST calls.
TX_SERVICE_URL=https://app.multisig.ledger.com/api/safe-transaction-service/11155111
SAFE_ADDRESS=0xYourSafeAddress
```

{% endtab %}
{% endtabs %}

The `TX_SERVICE_URL` points at the **Ledger-hosted** Transaction Service. Transactions proposed here appear in the [Ledger Multisig UI](https://app.multisig.ledger.com/).

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

## Helper: wait for receipt

Throughout this tutorial, we verify every on-chain transaction. This helper waits for the receipt and throws if the transaction reverted:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const publicClient = createPublicClient({
  chain: sepolia,
  transport: http(RPC_URL),
});

async function waitForReceipt(txHash: string) {
  const receipt = await publicClient.waitForTransactionReceipt({
    hash: txHash as `0x${string}`,
    timeout: 120_000,
  });
  if (receipt.status === "reverted") {
    throw new Error(`Transaction ${txHash} reverted on-chain`);
  }
  return receipt;
}
```

{% endtab %}

{% tab title="Python" %}

```python
def wait_for_receipt(tx_hash: str, timeout_s: int = 120) -> dict:
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout_s)
    # web3.py uses status: 1 (success) / 0 (revert)
    if receipt.get("status") == 0:
        raise RuntimeError(f"Transaction {tx_hash} reverted on-chain")
    return dict(receipt)
```

{% endtab %}

{% tab title="curl" %}

```http
POST {RPC_URL}
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_getTransactionReceipt",
  "params": ["0xOnChainTxHash"]
}
```

{% endtab %}
{% endtabs %}

## Helper: reinitialize Protocol Kit

After any on-chain state change (new owner, new threshold), reinitialize the Protocol Kit to pick up the latest Safe state:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
async function freshKit(signerKey: string) {
  return Safe.init({
    provider: RPC_URL,
    signer: signerKey,
    safeAddress: SAFE_ADDRESS,
  });
}
```

{% endtab %}

{% tab title="Python" %}

```python
# Python approach: don't cache Safe state.
# Re-fetch owners/threshold from the Transaction Service when needed.
def get_safe_info() -> dict:
    resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/")
    resp.raise_for_status()
    return resp.json()
```

{% endtab %}

{% tab title="curl" %}

```http
GET /v1/safes/{SAFE_ADDRESS}/
Accept: application/json
```

{% endtab %}
{% endtabs %}

## Helper: normalize private keys

Use one key format consistently. This helper accepts either `"abc..."` or `"0xabc..."`:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
function normalizePrivateKey(key: string): `0x${string}` {
  return (key.startsWith("0x") ? key : `0x${key}`) as `0x${string}`;
}
```

{% endtab %}

{% tab title="Python" %}

```python
def normalize_private_key(key: str) -> str:
    return key if key.startswith("0x") else f"0x{key}"
```

{% endtab %}

{% tab title="curl" %}

```http
# Keys must be normalized in your client before signing.
```

{% endtab %}
{% endtabs %}

## Step-by-step

{% stepper %}
{% step %}

### Add a second owner

Starting from a 1-of-1 Safe, generate an ephemeral Owner B key, then add Owner B while keeping the threshold at 1 (so Owner A can still execute alone for now).

{% tabs %}
{% tab title="TypeScript" %}

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

const ownerAAddress = privateKeyToAccount(normalizePrivateKey(OWNER_A_PRIVATE_KEY)).address;

// Generate a temporary second owner for this tutorial run
const ownerBKeyHex = Buffer.from(randomBytes(32)).toString("hex");
const ownerBAddress = privateKeyToAccount(normalizePrivateKey(ownerBKeyHex)).address;

let kitA = await freshKit(normalizePrivateKey(OWNER_A_PRIVATE_KEY));

const addOwnerTx = await kitA.createAddOwnerTx({
  ownerAddress: ownerBAddress,
  threshold: 1, // Keep threshold at 1 for now
});

// Sign, propose, and execute (threshold is still 1)
const safeTxHash = await kitA.getTransactionHash(addOwnerTx);
const signature = await kitA.signHash(safeTxHash);

await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: addOwnerTx.data,
  safeTxHash,
  senderAddress: ownerAAddress,
  senderSignature: signature.data,
});

const pendingTx = await apiKit.getTransaction(safeTxHash);
const result = await kitA.executeTransaction(pendingTx);
await waitForReceipt(result.hash);
```

{% endtab %}

{% tab title="Python" %}

```python
# Building Safe owner-management txs (add/remove owners, change threshold)
# requires correct Safe calldata + safeTxHash computation.
# Practical path:
# - build/sign/execute on-chain with the TypeScript Protocol Kit
# - use Python only for REST querying (shown in later steps)
```

{% endtab %}

{% tab title="curl" %}

```http
# Same constraint as Python:
# owner-management calldata + safeTxHash must be computed client-side.
```

{% endtab %}
{% endtabs %}

What happens on-chain: The Safe calls its own `addOwnerWithThreshold(owner, threshold)` method, adding Owner B to the owner list.

Verify the new owner was added:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
kitA = await freshKit(normalizePrivateKey(OWNER_A_PRIVATE_KEY));
const owners = await kitA.getOwners();
// owners now includes both Owner A and Owner B
```

{% endtab %}

{% tab title="Python" %}

```python
info = get_safe_info()
owners = info["owners"]
```

{% endtab %}

{% tab title="curl" %}

```http
GET /v1/safes/{SAFE_ADDRESS}/
Accept: application/json
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Change threshold to 2

Now require both owners to sign. This is an on-chain Safe transaction:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
kitA = await freshKit(normalizePrivateKey(OWNER_A_PRIVATE_KEY));

const changeThresholdTx = await kitA.createChangeThresholdTx(2);

const thresholdTxHash = await kitA.getTransactionHash(changeThresholdTx);
const thresholdSig = await kitA.signHash(thresholdTxHash);

await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: changeThresholdTx.data,
  safeTxHash: thresholdTxHash,
  senderAddress: ownerAAddress,
  senderSignature: thresholdSig.data,
});

const thresholdPendingTx = await apiKit.getTransaction(thresholdTxHash);
const thresholdResult = await kitA.executeTransaction(thresholdPendingTx);
await waitForReceipt(thresholdResult.hash);
```

{% endtab %}

{% tab title="Python" %}

```python
# Threshold change is an on-chain Safe tx.
# Use the TypeScript Protocol Kit to build/sign/execute it.
# Then poll the Transaction Service until it indexes the new threshold.
```

{% endtab %}

{% tab title="curl" %}

```http
# Threshold change is executed on-chain.
# Use the Transaction Service only for proposal + confirmations.
```

{% endtab %}
{% endtabs %}

Critical: After changing the threshold on-chain, you must wait for the Transaction Service to index the new state before proposing new transactions. Otherwise, `confirmationsRequired` in the API response will still show the old threshold.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
// Poll until the Transaction Service reports threshold = 2
async function waitForTxServiceThreshold(expected: number) {
  for (let attempt = 0; attempt < 24; attempt++) {
    await new Promise((r) => setTimeout(r, 5000));
    const info = await apiKit.getSafeInfo(SAFE_ADDRESS);
    if (info.threshold === expected) return;
  }
  throw new Error(`TX Service did not index threshold ${expected}`);
}

await waitForTxServiceThreshold(2);
```

{% endtab %}

{% tab title="Python" %}

```python
def wait_for_threshold(expected: int, attempts: int = 24, sleep_s: int = 5) -> None:
    for _ in range(attempts):
        time.sleep(sleep_s)
        info = get_safe_info()
        if int(info["threshold"]) == expected:
            return
    raise TimeoutError(f"TX Service did not index threshold {expected}")

wait_for_threshold(2)
```

{% endtab %}

{% tab title="curl" %}

```http
# Poll this endpoint until it returns the expected threshold:
GET /v1/safes/{SAFE_ADDRESS}/
Accept: application/json
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Propose a transaction (Owner A signs)

With threshold = 2, a single signature is no longer enough to execute. Owner A proposes and signs:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
kitA = await freshKit(normalizePrivateKey(OWNER_A_PRIVATE_KEY));

const transferTx = await kitA.createTransaction({
  transactions: [
    {
      to: ownerAAddress,
      value: "10000000000000", // 0.00001 ETH
      data: "0x",
      operation: OperationType.Call,
    },
  ],
});

const transferTxHash = await kitA.getTransactionHash(transferTx);
const signatureA = await kitA.signHash(transferTxHash);

await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: transferTx.data,
  safeTxHash: transferTxHash,
  senderAddress: ownerAAddress,
  senderSignature: signatureA.data,
});

// Check: 1 of 2 confirmations
let tx = await apiKit.getTransaction(transferTxHash);
// tx.confirmations.length === 1, tx.confirmationsRequired === 2
```

{% endtab %}

{% tab title="Python" %}

```python
# Query the proposed tx by safeTxHash:
safe_tx_hash = "0xYourSafeTxHash"
resp = session.get(f"{TX_SERVICE_URL}/v1/multisig-transactions/{safe_tx_hash}/")
resp.raise_for_status()
tx = resp.json()
```

{% endtab %}

{% tab title="curl" %}

```http
GET /v1/multisig-transactions/{safeTxHash}/
Accept: application/json
```

{% endtab %}
{% endtabs %}

REST API: `POST /v1/safes/{address}/multisig-transactions/`
{% endstep %}

{% step %}

### Confirm with Owner B

Owner B retrieves the pending transaction and adds their confirmation (off-chain signature):

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const kitB = await freshKit(ownerBKeyHex);
const signatureB = await kitB.signHash(transferTxHash);

await apiKit.confirmTransaction(transferTxHash, signatureB.data);

// Check: 2 of 2 confirmations
tx = await apiKit.getTransaction(transferTxHash);
// tx.confirmations.length === 2, tx.confirmationsRequired === 2
```

{% endtab %}

{% tab title="Python" %}

```python
safe_tx_hash = "0xYourSafeTxHash"
owner_b_signature = "0xOwnerBSignature"

resp = session.post(
    f"{TX_SERVICE_URL}/v1/multisig-transactions/{safe_tx_hash}/confirmations/",
    json={"signature": owner_b_signature},
)
resp.raise_for_status()
```

{% endtab %}

{% tab title="curl" %}

```http
POST /v1/multisig-transactions/{safeTxHash}/confirmations/
Accept: application/json
Content-Type: application/json

{ "signature": "0xOwnerSignature" }
```

{% endtab %}
{% endtabs %}

REST API: `POST /v1/multisig-transactions/{safeTxHash}/confirmations/`
{% endstep %}

{% step %}

### Execute (threshold met)

Either owner (or any account) can now execute the transaction, since both signatures are collected in the Transaction Service:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
tx = await apiKit.getTransaction(transferTxHash);

if ((tx.confirmations?.length ?? 0) >= tx.confirmationsRequired) {
  const execResult = await kitA.executeTransaction(tx);
  await waitForReceipt(execResult.hash);
}
```

{% endtab %}

{% tab title="Python" %}

```python
# Execution is an on-chain call to the Safe contract.
# The Transaction Service cannot execute for you.
#
# You can check if the threshold is met:
resp = session.get(f"{TX_SERVICE_URL}/v1/multisig-transactions/0xYourSafeTxHash/")
resp.raise_for_status()
tx = resp.json()
threshold_met = len(tx.get("confirmations") or []) >= tx["confirmationsRequired"]
```

{% endtab %}

{% tab title="curl" %}

```http
# Execution requires an on-chain tx (eth_sendRawTransaction).
# It cannot be done via the Transaction Service REST API.
```

{% endtab %}
{% endtabs %}

The Protocol Kit pulls the collected signatures from the transaction object and submits them together in the on-chain execution call.
{% endstep %}

{% step %}

### Reset (optional): remove owner and lower threshold

To return to a 1-of-1 configuration (e.g., after testing), remove Owner B and lower the threshold in a single transaction. This still requires both signatures since threshold is currently 2:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
kitA = await freshKit(normalizePrivateKey(OWNER_A_PRIVATE_KEY));

const removeOwnerTx = await kitA.createRemoveOwnerTx({
  ownerAddress: ownerBAddress,
  threshold: 1, // Lower threshold along with removing owner
});

const removeTxHash = await kitA.getTransactionHash(removeOwnerTx);
const removeSigA = await kitA.signHash(removeTxHash);

// Owner A proposes
await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  safeTransactionData: removeOwnerTx.data,
  safeTxHash: removeTxHash,
  senderAddress: ownerAAddress,
  senderSignature: removeSigA.data,
});

// Owner B confirms
const kitB2 = await freshKit(ownerBKeyHex);
const removeSigB = await kitB2.signHash(removeTxHash);
await apiKit.confirmTransaction(removeTxHash, removeSigB.data);

// Execute
const removePendingTx = await apiKit.getTransaction(removeTxHash);
const removeResult = await kitA.executeTransaction(removePendingTx);
await waitForReceipt(removeResult.hash);
```

{% endtab %}

{% tab title="Python" %}

```python
# removeOwner(...) is an on-chain Safe tx.
# It requires correct Safe calldata, including the Safe owners linked-list prevOwner.
# Recommended: execute the reset with the TypeScript Protocol Kit.
```

{% endtab %}

{% tab title="curl" %}

```http
# Same constraints as Python: reset requires an on-chain Safe tx.
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## Key concepts

How threshold signing works:

A Safe with `N` owners and threshold `T` requires at least `T` unique owner signatures before a transaction can execute.

| Phase   | Who                       | What happens                                               | Gas? |
| ------- | ------------------------- | ---------------------------------------------------------- | ---- |
| Propose | Any owner or delegate     | Transaction data + first signature submitted to TX Service | No   |
| Confirm | Other owners              | Additional signatures submitted to TX Service              | No   |
| Execute | Anyone (usually an owner) | All signatures bundled and submitted on-chain              | Yes  |

Signatures are collected off-chain (free) and only the final execution costs gas. This is the core efficiency of the Safe signature model.

For the full guide, see [Transactions with Off-chain Signatures](https://ledger-4.gitbook.io/ledger-multisig/guides/transactions-with-off-chain-signatures).

## Tips and pitfalls

* **Transaction Service indexing lag is critical here.** After any on-chain state change (adding an owner, changing threshold), the Transaction Service needs time (10–60 seconds) to index the new state. If you immediately propose a new transaction, the API may report the old `confirmationsRequired` value. Always poll `getSafeInfo` until it reflects the expected threshold before proceeding.
* **Always verify receipt status.** `executeTransaction` returns a hash even if the on-chain transaction reverts. Use `waitForTransactionReceipt` and check `receipt.status`:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
if (receipt.status === "reverted") {
  throw new Error("Transaction reverted");
}
```

{% endtab %}

{% tab title="Python" %}

```python
if receipt.get("status") == 0:
    raise RuntimeError("Transaction reverted")
```

{% endtab %}

{% tab title="curl" %}

```http
# Check `status` in the JSON-RPC receipt:
# - "0x1" = success
# - "0x0" = revert
```

{% endtab %}
{% endtabs %}

* **Reinitialize the Protocol Kit after state changes.** The Protocol Kit caches the Safe's owner list and threshold at initialization. After adding/removing owners or changing the threshold, call `Safe.init(...)` again to pick up the new state.
* **Private key format must be consistent.** Avoid mixing prefixed/non-prefixed keys manually (for example `0x` + `0x...`). Use a key normalizer helper and keep the format uniform.
* **Signature ordering.** The Safe contract expects signatures sorted by signer address (ascending, lowercase). The Protocol Kit and Transaction Service handle this automatically when you use `executeTransaction` with a transaction object from `getTransaction`. Don't manually reorder signatures.
* **`createRemoveOwnerTx` with threshold.** When removing an owner, you can simultaneously lower the threshold. If you set the threshold higher than the remaining owner count, the transaction will revert.
* **Don't lose keys mid-flow.** If you change the threshold to 2-of-2 and then lose access to one key, you'll be locked out of the Safe permanently. In production, always test threshold changes carefully and keep backup access plans.


# 7. Troubleshooting

Common integration failures and fixes for the Ledger Multisig API tutorials.

<details>

<summary><code>TypeError: SafeApiKit is not a constructor</code></summary>

#### Cause

ESM/CJS interop differences in `@safe-global/api-kit` and `@safe-global/protocol-kit`.

#### Fix

Use interop-safe constructor resolution:

```typescript
import SafeApiKitModule from "@safe-global/api-kit";

const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;
```

Apply the same pattern for `SafeModule` from `@safe-global/protocol-kit`.

</details>

<details>

<summary><code>isExecuted</code> is <code>false</code> right after execution</summary>

#### Cause

Transaction Service indexing lag. Execution is on-chain, but API indexing is delayed.

#### Fix

* Wait 10-60 seconds, then query again.
* Verify by `safeTxHash` directly (`getTransaction(safeTxHash)`), not by "latest tx".

</details>

<details>

<summary><code>executeTransaction</code> returns hash but tx failed</summary>

#### Cause

`executeTransaction` can return a submitted hash even when the EVM call reverts.

#### Fix

Always wait for receipt and check status:

```typescript
const receipt = await publicClient.waitForTransactionReceipt({
  hash: executionResult.hash as `0x${string}`,
  timeout: 120_000,
});

if (receipt.status === "reverted") {
  throw new Error("Transaction reverted on-chain");
}
```

</details>

<details>

<summary>Wrong <code>confirmationsRequired</code> after threshold/owner change</summary>

#### Cause

Safe state changed on-chain, but Transaction Service still serves stale indexed state.

#### Fix

Poll `getSafeInfo` until `threshold` matches expected value before proposing the next tx in the flow.

</details>

<details>

<summary><code>getSafeBalances</code> method missing</summary>

#### Cause

`@safe-global/api-kit` v2.5.7 does not wrap balances.

#### Fix

Use direct HTTP request:

```http
GET /v2/safes/{address}/balances/
```

under the configured base URL:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

</details>

<details>

<summary>Private key formatting issues (<code>0x0x...</code>)</summary>

#### Cause

Mixing prefixed and non-prefixed private key formats.

#### Fix

Normalize once:

```typescript
function normalizePrivateKey(key: string): `0x${string}` {
  return (key.startsWith("0x") ? key : `0x${key}`) as `0x${string}`;
}
```

</details>

<details>

<summary>RPC instability on Sepolia</summary>

#### Cause

Public Sepolia RPC endpoints can be slow or timeout.

#### Fix

* Retry with backoff.
* Use a dedicated provider (Alchemy/Infura/QuickNode) for stable integration tests.

</details>

<details>

<summary>URL confusion: public Safe service vs Ledger-hosted service</summary>

#### Cause

Using public Safe Transaction Service endpoints by mistake.

#### Fix

Use Ledger-hosted base URL:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

Transactions proposed through this backend appear in Ledger Multisig UI.

</details>

## See also

* [Tutorial index](broken://pages/907a98d9c91d9a38396ddcc14d1b90a305f95138)
* [Transactions with Off-chain Signatures](https://ledger-4.gitbook.io/ledger-multisig/guides/transactions-with-off-chain-signatures)


# CLI Guides


# CLI Overview

The Ledger Enterprise Multisig CLI (`lem`) is a command-line interface that lets you operate Safe multisig wallets directly from your shell. Query Safes, propose transactions, collect signatures, and execute on-chain using either a Ledger hardware device or a software signer.

`lem` covers the full Safe transaction lifecycle and produces structured JSON on stdout for every command, which makes it equally suitable for:

* **Human operators** automating recurring treasury operations or scripting incident response.
* **AI agents** that need a discoverable, predictable, machine-parseable surface for Ledger Multisig.

It is the same Ledger-secured multisig platform documented in the [API Overview](https://help.multisig.ledger.com/) and [Guides](https://help.multisig.ledger.com/guides), exposed through a different surface.

### What you can do

| Area               | Commands                                                                |
| ------------------ | ----------------------------------------------------------------------- |
| Signer setup       | `lem` connect, `lem` config show, `lem` config path                     |
| Query Safes        | `lem` safe scan, `lem` safe info, `lem` safe balances, `lem` safe nonce |
| Query transactions | `lem` tx list, `lem` tx show                                            |
| Sign & execute     | `lem` tx propose, `lem` tx sign, `lem` tx execute                       |

With these commands you can run the **entire** Safe transaction lifecycle, from proposal to on-chain execution, without ever leaving the shell. See **Proposing, signing & executing** for the end-to-end walkthrough.

### Architecture

`lem` is a thin wrapper around the same components used by the existing TypeScript and Python guides:

* The **Safe API Kit** for Transaction Service reads and proposals.
* The **Safe Protocol Kit** for transaction creation, signing, and on-chain execution.
* The **Ledger Device Management Kit** for hardware signing over USB.

All operations target Ledger's hosted Transaction Service, so any Safe or transaction you touch via `lem` appears in the [Ledger Enterprise Multisig UI](https://app.multisig.ledger.com/) and in API queries, and vice versa. The JSON output of every command is the same shape returned by the underlying SDK / REST API, so anything you can build with the SDKs you can build with the CLI.

### Supported networks

The CLI supports the same chains as the rest of the platform. See [Supported Networks](https://help.multisig.ledger.com/supported-networks). At the time of writing:

| Network                    | Chain ID |
| -------------------------- | -------- |
| Ethereum Mainnet           | 1        |
| Optimism                   | 10       |
| BSC                        | 56       |
| Base                       | 8453     |
| Arbitrum                   | 42161    |
| Ethereum Sepolia (testnet) | 11155111 |

### Next steps

* **Installation**
* **Quickstart**


# Installation

Install the Ledger Enterprise Multisig CLI from npm.

### What you'll learn

* Install `lem` globally or invoke it ad-hoc with `npx`.
* Verify the installation.
* Set up the prerequisites for hardware signing on a Ledger device.

### Prerequisites

* **Node.js 22+** (run node --version to check).
* **macOS, Linux, or Windows**. On Linux, USB access to a Ledger device requires the [Ledger udev rules](https://support.ledger.com/article/115005165269-zd).
* **Ledger device** with the Ethereum app installed (only required when signing with hardware. Read-only commands like `lem safe info` work without one).

### Install

**Global install (recommended)**

```
npm install -g les-multisig-cli
```

After install, `lem` is available on your `$PATH`.

```
lem --version
```

**npx (no install)**

```
npx les-multisig-cli@latest --version
```

Use this for one-off invocations or CI jobs that don't want to maintain a global install.

**Build from source**

For contributors or anyone who needs to run an unreleased build:

```
git clone https://github.com/LedgerHQ/les-multisig.git
cd les-multisig
pnpm install
pnpm --filter les-multisig-cli build
ln -sf "$(pwd)/packages/cli/dist/index.mjs" ~/.local/bin/lem
```

### Verify

Run any read-only command. No device or config is required:

```
lem --help
lem safe scan --help
```

`lem --version` should print the installed CLI version.

### Ledger device setup

Before running commands that sign or execute transactions:

1. Plug your Ledger device into a USB port.
2. Unlock it with your PIN.
3. Open the **Ethereum** app on the device.
4. Make sure **Blind signing** is enabled if you plan to sign payloads that contain calldata the Ethereum app cannot decode natively. Ledger Multisig surfaces clear-signing where it can.

Your device must be unlocked and on the Ethereum app each time `lem` talks to it.

### Tips and pitfalls

* **No device, no problem.** Read-only commands (`safe scan`, `safe info`, `safe balances`, `safe nonce`, `tx list`, `tx show`) do not need a connected device or a `lem connect` session.
* **Don't run as root.** USB access on Linux should be granted via udev rules, not by running `lem` with `sudo`.
* **Conflicting installs.** If `lem --version` doesn't match what you just installed, check for a stale build symlinked into `~/.local/bin` or another directory ahead of npm's global bin on `$PATH`.

### Next steps

* **Quickstart**
* **Configuring a signer**


# Quickstart

End-to-end: connect a Ledger, find a Safe you own, propose an ETH transfer, collect a co-owner signature, and execute on-chain, all from the shell.

### What you'll learn

* Connect a signing identity with `lem connect`.
* Discover Safes owned by your signer.
* Propose, co-sign, and execute a Safe transaction.
* Verify execution through the Transaction Service.

### Prerequisites

* `lem` installed (see **Installation**).
* A Ledger device, unlocked, with the Ethereum app open.
* A Safe on Sepolia where the connected signer is an owner.
* A second owner who can sign (a second Ledger, or, for testing, a software signer).
* A bit of Sepolia ETH in the Safe for gas and the transfer amount.

> Use **Sepolia** for your first run. Mainnet runs the same commands with --chain-id 1.

### Step-by-step

#### 1. Connect your signer

Pair your Ledger and cache the signer address + derivation path in \~/.config/les-multisig/config.json.

```
lem connect
```

Confirm the address on the device when prompted. After this completes, every subsequent command picks up the same signing identity automatically.

#### 2. Discover Safes you own

```
lem safe scan --pretty
```

The output lists Safes the connected signer is an owner of, grouped by chain. Pick a Safe on chainId 11155111 (Sepolia) for the rest of the quickstart and copy its address.

#### 3. Inspect the Safe

```
lem safe info \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --pretty
```

Note threshold, owners, and nonce. You'll need to know how many signatures are required before the transaction can be executed.

#### 4. Propose a transaction

Propose an ETH transfer. The value is in wei (the example below sends 0.0001 ETH).

```
lem tx propose \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --to 0xRecipientAddress \
  --value 100000000000000
```

You will be asked to confirm the transaction on your Ledger. After approval, `lem` prints the proposal payload and the `safeTxHash`. Copy the `safeTxHash`. That is the identifier for this transaction inside the Safe.

The transaction is now visible in the [Ledger Enterprise Multisig UI](https://app.multisig.ledger.com/) for all other owners.

#### 5. Collect a second signature

Switch to a second owner (a second Ledger user, or run the command with a --seed/--salt software signer on a testnet) and sign the same safeTxHash:

```
lem tx sign --chain-id 11155111 0xYourSafeTxHash
```

`lem tx sign` automatically refuses already-executed transactions, transactions that already have enough signatures, and signers who have already confirmed.

Check confirmation progress at any time with:

```
lem tx show --safe-tx-hash 0xYourSafeTxHash --pretty
```

Move on once confirmations.length === confirmationsRequired.

#### 6. Execute on-chain

Once the threshold is met, any owner can submit the transaction on-chain (and pays gas):

```
lem tx execute --chain-id 11155111 0xYourSafeTxHash
```

The command returns the on-chain transactionHash along with the safeTxHash.

#### 7. Verify execution

The Transaction Service indexer typically catches up within 10–60 seconds after on-chain inclusion. Re-fetch the transaction to confirm:

```
sleep 15
lem tx show --safe-tx-hash 0xYourSafeTxHash --pretty
```

You should see isExecuted: true, isSuccessful: true, and the on-chain transactionHash.

### Key concepts

> **Off-chain proposal, on-chain execution.** `lem tx propose` and `lem tx sign` are gas-free. They only update the Transaction Service. `lem tx execute` is the one command that submits the bundled signatures on-chain and pays gas. A 3-of-5 Safe still only pays gas once, on the final execute.

### Tips and pitfalls

* **Indexer lag.** If `lem tx show` returns `isExecuted: false` right after `lem tx execute`, wait 10–60 seconds and retry. The on-chain transaction is already confirmed. The indexer just has not caught up.
* **Execute can return a hash for a reverted tx.** `lem tx execute` returns whatever the on-chain receipt yields. Always check `isSuccessful` (or the receipt on a block explorer) before declaring victory.
* **Fund your executor.** The signer running `lem tx execute` needs native gas on the target chain.
* **Confirm on-device every time.** Hardware signing requires a manual confirmation on the Ledger for both tx propose and tx sign.


# Configuring a signer

`lem` supports three signer modes: a Ledger hardware wallet over USB, which is the default and the only mode recommended for production, and two software signers reserved for testing and automation. This page covers how to pick a mode, how to inspect the resulting configuration, and the global flags that apply to every command.

### What you'll learn

* The three ways `lem` can produce signatures.
* How to pair a signer with `lem connect` and where the result is cached.
* Global flags that apply to every command.
* Security considerations for software signers.

### Signer modes

Selected at the command line for any command that produces a signature (connect, tx propose, tx sign, tx execute).

| Flag                               | Mode                                                                | When to use                        |
| ---------------------------------- | ------------------------------------------------------------------- | ---------------------------------- |
| *(none)*                           | USB Ledger hardware wallet                                          | Default. **Required for mainnet.** |
| --seed "\<mnemonic>"               | Software signer from a BIP-39 mnemonic                              | Testing, scripted demos.           |
| --salt \<string> --index \<number> | Software signer from a deterministic seed derived from salt + index | CI / non-interactive agents.       |

`--seed` is mutually exclusive with `--salt` / `--index`. The CLI rejects combined usage with `--seed cannot be combined with --salt or --index` (exit code 2).

> 🚨 **Danger:** Never use --seed or --salt/--index with a key that owns mainnet funds. Software signer flags exist for testnet and automated test environments only. For mainnet, stay on USB.

### Step-by-step

#### 1. Pair your signer

Pairing once writes the signer's address and derivation path into a local config file so that subsequent commands don't have to re-prompt the device.

**USB Ledger (default)**

```
lem connect
```

Confirm the address on the device when prompted.

**--seed (mnemonic)**

```
lem connect --seed "test test test test test test test test test test test junk"
```

The mnemonic above is the well-known Hardhat/Anvil test mnemonic. Do not use it outside of local testing.

**--salt + --index**

```
lem connect --salt ci-sepolia-2 --index 0
```

The salt + index combination is hashed into a deterministic mnemonic. Different salt values produce different signers. `--index` lets you derive multiple signers from the same salt.

#### 2. Inspect the cached configuration

```
lem config show --pretty
```

Returns the current signer address and derivation path, plus the env vars `lem` has loaded:

```
{
  "config": {
    "address": "0xYourSignerAddress",
    "derivationPath": "44'/60'/0'/0/0"
  },
  "env": {
    "RPC_NODE_SEPOLIA_URL": "https://...",
    "SAFE_TRANSACTION_SERVICE_SEPOLIA_URL": "https://..."
  }
}
```

To find the file on disk:

```
lem config path
# /Users/you/.config/les-multisig/config.json
```

The file is created with `0600` permissions in `~/.config/les-multisig/config.json`. To switch signers, just run `lem connect` again. It overwrites the file.

#### 3. Use the signer

Once `lem connect` has been run, every command that needs the signer picks it up automatically. You do not need to repeat the device flag on every command unless you want to override the cached identity for a single invocation.

```
lem tx sign --chain-id 11155111 0xYourSafeTxHash
```

If a signer has not been paired yet, commands that need one fail with exit code `3` (`CONFIG_ERROR`) and a `Run connect to initialize a signing identity` suggestion.

### Global options

These flags work on every command and can be combined with command-specific flags. See the **Command reference** for the per-command flag matrix.

| Flag          | Type   | Default | Description                                                                                                          |
| ------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------- |
| --debug       | bool   | false   | Verbose logs to stderr (the JSON result still goes to stdout).                                                       |
| --timestamps  | bool   | false   | Prefix log lines with ISO-8601 timestamps.                                                                           |
| --pretty      | bool   | false   | Pretty-print the JSON output on stdout. Useful for humans; agents should leave it off so output is compact.          |
| --version, -v | bool   | false   | Print the CLI version and exit.                                                                                      |
| --env \<path> | string | .env    | Path to an env file to load before running.                                                                          |
| --help, -h    | bool   | false   | Display help for the current command. Works at any level (`lem --help`, `lem safe --help`, `lem tx propose --help`). |

### Tips and pitfalls

* `lem config show` does not print mnemonics. It only shows the signer address, derivation path, and known env vars. Mnemonics are never persisted.
* **Re-running** `lem connect` overwrites the cached identity. That is by design. There is no concept of "logout".
* **Env file scope.** `--env` defaults to `.env` in the current working directory. Distributed binaries can ship with sensible defaults baked in, so you usually do not need an env file for hosted Ledger services. You only need one for self-hosted endpoints.
* **Software signers and CI.** When deriving signers via `--salt + --index`, store the salt itself in a real secret manager. Anyone with the salt and index can sign.

### Next steps

* **Querying Safe data**
* **Proposing, signing & executing**


# Querying Safe data

Read-only commands to inspect Safes and transactions. Nothing on this page consumes a signer or costs gas. You can run these commands without a Ledger and without ever calling `lem connect`, with one exception (`lem safe scan`, which needs the cached signer address to know what to scan for).

This page is the CLI counterpart to [1. Querying Safe Data](https://help.multisig.ledger.com/guides/1.-querying-safe-data) and produces the same JSON shapes as the underlying Safe API Kit.

### What you'll learn

* List Safes owned by the connected signer.
* Fetch Safe configuration, balances, and next nonce.
* List pending and historical transactions for a Safe.
* Fetch a specific transaction by its safeTxHash.

### Prerequisites

* `lem` installed (see **Installation**).
* For `lem safe scan`: a paired signer (see **Configuring a signer**). Every other command on this page only needs a Safe address.

### Step-by-step

#### 1. Scan Safes you own

Lists every Safe (across every supported chain) where the connected signer is an owner.

```
lem safe scan --pretty
```

Output:

```
{
  "address": "0xYourSignerAddress",
  "safes": [
    { "address": "0xSafeOnSepolia", "chainId": 11155111 },
    { "address": "0xSafeOnMainnet", "chainId": 1 }
  ]
}
```

This is the standard way to discover Safes from an agent harness. Feed the output into whichever workflow comes next.

#### 2. Get Safe info

Retrieve owners, threshold, on-chain nonce, version, modules, fallback handler, and guard.

```
lem safe info \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --pretty
```

The shape mirrors the Safe Client Gateway's Safe info response and matches what apiKit.getSafeInfo() returns in the TS guides.

#### 3. Fetch balances

Native ETH + ERC-20 balances held by the Safe.

```
lem safe balances \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --pretty
```

Each entry contains `tokenAddress`, `token` (with symbol and decimals), and a raw integer balance. Format to a human-readable value using the decimals field. See [Guide 1, step 3](https://help.multisig.ledger.com/guides/1.-querying-safe-data) for a worked example.

#### 4. Get the next nonce

Returns the next Safe nonce, accounting for any pending (not-yet-executed) transactions. Use this when you want to queue a new transaction behind everything already proposed.

```
lem safe nonce \
  --address 0xYourSafeAddress \
  --chain-id 11155111
```

Output is a single integer printed to stdout. It is easy to capture into a shell variable:

```
NEXT=$(lem safe nonce --address 0xSafe --chain-id 11155111)
```

#### 5. List transactions

By default `lem tx list` returns only **pending** transactions. This is most useful for "what needs my signature?" workflows. Pass `--all` for the full history.

```
# Pending only
lem tx list \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --pretty

# Including executed & rejected
lem tx list \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --all \
  --pretty
```

`--limit` and `--offset` work for pagination. Each result includes `safeTxHash`, `nonce`, `to`, `value`, `data`, `isExecuted`, `isSuccessful`, and the array of confirmations collected so far.

#### 6. Show a transaction

Fetch the full record for one transaction by its safeTxHash.

```
lem tx show --safe-tx-hash 0xYourSafeTxHash --pretty
```

The output is identical to a Safe API Kit getTransaction response: full transaction data, every collected confirmation (with signer address and signature), and the execution status / on-chain hash once executed.

### Key concepts

> **The Safe Transaction Service is an off-chain index.** It tracks on-chain Safe state (owners, threshold, nonce) and stores proposals before they hit chain. `lem` queries Ledger's hosted instance of this service, so any transaction proposed via the Ledger Multisig UI is visible to the CLI, and vice versa.

### Tips and pitfalls

* **Pipe everything through** `jq`. `lem` prints minified JSON by default, so commands compose cleanly:

  ```
  lem safe info --address 0xSafe --chain-id 1 | jq '.threshold'
  lem tx list --address 0xSafe --chain-id 1 | jq '.results[].safeTxHash'
  ```
* **Indexer lag.** After an execute, expect 10–60 seconds before `lem tx show` flips `isExecuted` to `true`.
* **Pagination.** Large Safes can have many transactions. `lem tx list --all --limit 50 --offset 0` is the right way to page through history.

### Next steps

* **Proposing, signing & executing**
* **Command reference**


# Proposing, signing and executing

This page is the CLI counterpart to [2. Transaction Lifecycle](https://help.multisig.ledger.com/guides/2.-transaction-lifecycle-including-off-chain-signatures), covering the same four phases: propose, collect signatures, execute, and verify, as shell commands instead of TypeScript / Python / curl.

### What you'll learn

* Propose a Safe transaction with `lem tx propose`.
* Have additional owners add their signatures with `lem tx sign`.
* Submit the transaction on-chain with `lem tx execute`.
* Verify execution and handle indexer lag.

### Prerequisites

* `lem` installed (see **Installation**).
* A paired signer (see **Configuring a signer**).
* A Safe where the connected signer is an owner.
* The Safe funded with enough native gas for execution.

### Step-by-step

#### 1. Propose a transaction

Build, sign, and submit a new Safe transaction in a single command. The connected signer is recorded as the proposer, and their signature counts as the first confirmation.

```
lem tx propose \
  --address 0xYourSafeAddress \
  --chain-id 11155111 \
  --to 0xRecipientAddress \
  --value 100000000000000
```

* \--address: the Safe.
* `--chain-id`: numeric chain ID. See the **Overview** page for supported networks.
* \--to: the recipient (any EVM address).
* \--value: the amount, in **wei**, sent from the Safe.

`lem` will:

1. Resolve the next nonce automatically (accounts for queued proposals).
2. Ask your signer to approve the EIP-712 hash (on-device for USB Ledger).
3. POST the proposal + signature to the Transaction Service.
4. Print the canonical payload, including the safeTxHash:

```
{
  "safeAddress": "0xYourSafeAddress",
  "safeTransactionData": {
    "to": "0xRecipientAddress",
    "value": "100000000000000",
    "data": "0x",
    "operation": 0,
    "nonce": "12",
    "safeTxGas": "0",
    "baseGas": "0",
    "gasPrice": "0",
    "gasToken": "0x0000000000000000000000000000000000000000",
    "refundReceiver": "0x0000000000000000000000000000000000000000"
  },
  "safeTxHash": "0xYourSafeTxHash",
  "senderAddress": "0xYourSignerAddress",
  "senderSignature": "0x...",
  "origin": "{\"app\":\"les-multisig-cli\"}"
}
```

Capture the `safeTxHash`. That is the handle for everything that follows.

> **Today** `lem tx propose` is scoped to ETH transfers. For richer payloads (ERC-20 transfers, contract calls, MultiSend batches), use the Ledger Multisig UI or the API Kit. See [Guide 3](https://help.multisig.ledger.com/guides/3.-batch-transactions) and [Guide 5](https://help.multisig.ledger.com/guides/5.-erc20-token-transfers).

#### 2. Collect signatures

Other Safe owners run `lem tx sign` to confirm the same `safeTxHash`. They need their own `lem connect` session.

```
lem tx sign --chain-id 11155111 0xYourSafeTxHash
```

The signer is asked to confirm on-device. After approval, `lem` POSTs the new confirmation to the Transaction Service and prints the updated count:

```
{
  "safeTxHash": "0xYourSafeTxHash",
  "safeAddress": "0xYourSafeAddress",
  "signer": "0xCoOwnerAddress",
  "confirmations": 2,
  "confirmationsRequired": 2
}
```

`lem tx sign` short-circuits with a clear error in three situations:

| Situation                     | Behaviour                                                             |
| ----------------------------- | --------------------------------------------------------------------- |
| Transaction already executed  | Exit code 1, message Transaction has already been executed.           |
| Threshold already met         | Exit code 1, message Transaction already has enough signatures (N/M). |
| This signer already confirmed | Exit code 1, message This owner has already signed the transaction.   |

Check progress at any time with `lem tx show --safe-tx-hash 0xYourSafeTxHash --pretty`. Move on when `confirmations.length === confirmationsRequired`.

#### 3. Execute on-chain

With the threshold met, any owner can bundle the collected signatures and submit on-chain:

```
lem tx execute --chain-id 11155111 0xYourSafeTxHash
```

The executing signer pays gas, and `lem` returns the on-chain `transactionHash` plus the `safeTxHash` it executed:

```
{
  "safeTxHash": "0x...",
  "transactionHash": "0x...",
  "status": "submitted"
}
```

The same hardware-confirmation pattern applies: the device prompts you to approve the on-chain submission. From this point the transaction is at the mercy of the EVM. `lem` does not wait for inclusion by default.

#### 4. Verify execution

The Transaction Service typically catches up within 10–60 seconds. Re-fetch the transaction:

```
sleep 15
lem tx show --safe-tx-hash 0xYourSafeTxHash --pretty
```

Confirm `isExecuted: true`, `isSuccessful: true`, and that `transactionHash` matches what `lem tx execute` returned.

### Key concepts

> **Off-chain proposal, on-chain execution.** Propose and sign only update the Transaction Service. They are gas-free, idempotent, and visible across the platform. Execute is the single moment gas is spent and chain state changes.

> **Determinism via** `safeTxHash`. Every command that mutates a transaction takes `--safe-tx-hash` (or accepts it positionally for `tx sign` / `tx execute` / `tx show`). The hash is computed locally during propose and is independent of any RPC.

### Tips and pitfalls

* **Indexer lag is real.** If `lem tx show` reports `isExecuted: false` 5 seconds after `lem tx execute`, that is normal. Wait and retry.
* **A returned** `transactionHash` can still be a revert. `lem tx execute` reports whatever the receipt yields. Always inspect `isSuccessful` in the post-execute `tx show` output, or pull the receipt with your usual RPC tooling, before declaring victory.
* **Fund the executor.** The signer running `lem tx execute` must have native gas on the target chain. The Safe's balance is for the transfer. The executor's balance is for gas.
* **Ordering by nonce.** Safe enforces strict nonce ordering. If you queue tx A (nonce 7) and tx B (nonce 8) and execute B first, B will revert. Use `lem safe nonce` and `lem tx list` to keep ordering straight.
* **Re-running** `lem tx execute` is a no-op once on-chain. If the transaction is already executed, the Transaction Service will not re-submit it. `lem` will surface that as an error.

### Next steps

* **Command reference**
* **Building agent workflows**


# Command reference

Exhaustive flag, environment-variable, and exit-code reference for every \`lem\` command. For task-oriented walkthroughs, see Querying Safe data and Proposing, signing & executing.

### Synopsis

```
lem [GLOBAL OPTIONS] <command> [SUBCOMMAND] [OPTIONS]
```

Top-level commands: connect, config, safe, tx.

You can always append `--help` (or `-h`) at any level for inline help, for example `lem safe --help` or `lem tx propose --help`.

### Global options

Apply to every command.

| Flag          | Type   | Default | Description                                |
| ------------- | ------ | ------- | ------------------------------------------ |
| --debug       | bool   | false   | Verbose logs to stderr.                    |
| --timestamps  | bool   | false   | Prefix log lines with ISO-8601 timestamps. |
| --pretty      | bool   | false   | Pretty-print JSON output on stdout.        |
| --version, -v | bool   | false   | Print the CLI version and exit.            |
| --env \<path> | string | .env    | Env file to load before running.           |
| --help, -h    | bool   | false   | Display help.                              |

### Signer flags

Accepted by every command that produces a signature: connect, tx propose, tx sign, tx execute.

| Flag                               | Description                                                                                    |
| ---------------------------------- | ---------------------------------------------------------------------------------------------- |
| *(none)*                           | USB Ledger hardware wallet (default; required for mainnet).                                    |
| --seed "\<mnemonic>"               | Software signer from a BIP-39 mnemonic. Mutually exclusive with --salt/--index.                |
| --salt \<string> --index \<number> | Software signer derived deterministically from salt + index. Both flags are required together. |

See **Configuring a signer** for security guidance.

### Commands

#### `lem connect`

Pair a signing device and cache the resulting signer address + derivation path in \~/.config/les-multisig/config.json.

```
lem connect [SIGNER FLAGS]
```

No command-specific options. Accepts the signer flags above.

#### `lem config show`

Print the cached signer config and the env vars `lem` has loaded.

```
lem config show [--pretty]
```

Output: { "config": { "address", "derivationPath" }, "env": { ... } }. Mnemonics are never printed.

#### `lem config path`

Print the absolute path of the config file.

```
lem config path
```

#### `lem safe scan`

List Safes owned by the connected signer across every supported chain. Requires `lem connect` to have run first.

```
lem safe scan [--pretty]
```

#### `lem safe info`

Fetch on-chain configuration for a Safe (owners, threshold, nonce, version, modules, fallback handler, guard).

```
lem safe info --address <safe> --chain-id <id> [--pretty]
```

| Flag       | Required | Description           |
| ---------- | -------- | --------------------- |
| --address  | yes      | Safe address.         |
| --chain-id | yes      | Numeric EVM chain ID. |

#### `lem safe balances`

Fetch native + ERC-20 token balances for a Safe.

```
lem safe balances --address <safe> --chain-id <id> [--pretty]
```

Same options as `lem safe info`.

#### `lem safe nonce`

Return the next nonce to use when proposing a new transaction. Accounts for both executed and pending transactions.

```
lem safe nonce --address <safe> --chain-id <id>
```

Output is a single integer printed to stdout (not wrapped in JSON).

#### `lem tx list`

List transactions for a Safe. Defaults to **pending only**.

```
lem tx list \
  --address <safe> \
  --chain-id <id> \
  [--all] [--limit <n>] [--offset <n>] [--pretty]
```

| Flag       | Required | Default | Description                               |
| ---------- | -------- | ------- | ----------------------------------------- |
| --address  | yes      | n/a     | Safe address.                             |
| --chain-id | yes      | n/a     | Numeric chain ID.                         |
| --all      | no       | false   | Include executed + rejected transactions. |
| --limit    | no       | n/a     | Max results per page.                     |
| --offset   | no       | n/a     | Initial offset for pagination.            |

#### `lem tx show`

Fetch a single transaction by safeTxHash.

```
lem tx show --safe-tx-hash <hash> [--pretty]
```

\--safe-tx-hash is also the default positional argument, so this form works too:

```
lem tx show <hash> --chain-id <id>
```

#### `lem tx propose`

Build, sign, and submit a new Safe transaction. Today this command is scoped to native ETH transfers. For richer payloads, use the Ledger Multisig UI or the API Kit.

```
lem tx propose \
  --address <safe> \
  --chain-id <id> \
  --to <recipient> \
  --value <wei> \
  [SIGNER FLAGS]
```

| Flag       | Required | Description                 |
| ---------- | -------- | --------------------------- |
| --address  | yes      | Safe address.               |
| --chain-id | yes      | Numeric chain ID.           |
| --to       | yes      | Destination address.        |
| --value    | yes      | Amount to send, in **wei**. |

#### `lem tx sign`

Add a signature to an existing Safe transaction.

```
lem tx sign --chain-id <id> <safeTxHash> [SIGNER FLAGS]
```

The safeTxHash is the default positional argument and can also be passed as --safe-tx-hash.

Refuses to run when the transaction is already executed, the threshold is already met, or the connected signer has already confirmed.

#### `lem tx execute`

Submit a Safe transaction on-chain once the signature threshold is met. The executing signer pays gas.

```
lem tx execute --chain-id <id> <safeTxHash> [SIGNER FLAGS]
```

The safeTxHash is the default positional argument and can also be passed as --safe-tx-hash.

Output:

```
{
  "safeTxHash": "0x...",
  "transactionHash": "0x...",
  "status": "submitted"
}
```

### Environment variables

`lem` ships with sane defaults for Ledger-hosted services in the distributed binary. You only need to set env vars when pointing the CLI at self-hosted or custom endpoints. Set them in your shell or in the file passed via `--env`.

| Variable                                  | Purpose                                                                                           |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------- |
| SAFE\_CLIENT\_GATEWAY\_URL                | Safe Client Gateway endpoint (used by `lem safe scan`, `lem safe info`, and `lem safe balances`). |
| SAFE\_TRANSACTION\_SERVICE\_MAINNET\_URL  | Transaction Service for Ethereum Mainnet (chainId 1).                                             |
| SAFE\_TRANSACTION\_SERVICE\_OPTIMISM\_URL | Transaction Service for Optimism (chainId 10).                                                    |
| SAFE\_TRANSACTION\_SERVICE\_BSC\_URL      | Transaction Service for BSC (chainId 56).                                                         |
| SAFE\_TRANSACTION\_SERVICE\_BASE\_URL     | Transaction Service for Base (chainId 8453).                                                      |
| SAFE\_TRANSACTION\_SERVICE\_ARBITRUM\_URL | Transaction Service for Arbitrum (chainId 42161).                                                 |
| SAFE\_TRANSACTION\_SERVICE\_SEPOLIA\_URL  | Transaction Service for Sepolia (chainId 11155111).                                               |
| RPC\_NODE\_MAINNET\_URL                   | RPC node for Ethereum Mainnet.                                                                    |
| RPC\_NODE\_OPTIMISM\_URL                  | RPC node for Optimism.                                                                            |
| RPC\_NODE\_BSC\_URL                       | RPC node for BSC.                                                                                 |
| RPC\_NODE\_BASE\_URL                      | RPC node for Base.                                                                                |
| RPC\_NODE\_ARBITRUM\_URL                  | RPC node for Arbitrum.                                                                            |
| RPC\_NODE\_SEPOLIA\_URL                   | RPC node for Sepolia.                                                                             |

### Exit codes

Every command exits with one of these codes. `lem` also emits a structured JSON error to stdout on failure, which makes agent integrations straightforward. See **Building agent workflows**.

| Code | Name              | Meaning                                                                                        |
| ---- | ----------------- | ---------------------------------------------------------------------------------------------- |
| 0    | SUCCESS           | Command succeeded.                                                                             |
| 1    | GENERAL\_ERROR    | Runtime failure (RPC error, signature rejected on device, transaction already executed, etc.). |
| 2    | VALIDATION\_ERROR | Invalid or missing flags. Suggestion: Check '--help' for usage.                                |
| 3    | CONFIG\_ERROR     | No paired signer / corrupt config. Suggestion: Run connect to initialize a signing identity.   |
| 4    | ENV\_ERROR        | Required env var missing or malformed.                                                         |

Example failure payload on stdout:

```
{
  "error": {
    "code": "CONFIG_ERROR",
    "message": "No session found. Please run `device` first.",
    "suggestion": "Run connect to initialize a signing identity."
  }
}
```

### Aliases

The CLI ships with two binary names. They are identical:

```
lem                          # short form, recommended
ledger-enterprise-multisig   # long form, for environments where `lem` collides
```

### Next steps

* **Building agent workflows**


# Building agent workflows

\`lem\` works well for AI agents. This page covers why it fits tool use and how to wire it into Bash, Python, and MCP workflows.

### What you'll learn

* Why `lem` is a good fit for agent tool-use.
* How to drive `lem` non-interactively in a sandbox.
* Two end-to-end recipes (Bash and Python) for proposing and signing transactions from an agent loop.
* How to expose `lem` to MCP-aware agents.

### Why `lem` is agent-friendly

| Property                                  | What it means for agents                                                                                                                                                                        |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **JSON-by-default output**                | Every command prints a single JSON document to stdout. Feed it straight into the next tool-call without parsing prose. Pass `--pretty` only when a human will read it.                          |
| **Structured errors on the same channel** | Failures print `{ "error": { "code", "message", "suggestion" } }` to stdout and set a non-zero exit code. Agents can branch on `error.code` instead of regexing English.                        |
| **Stable exit codes**                     | `0` success, `2` validation, `3` no signer paired, `4` env misconfig, and `1` everything else. See the exit codes table in **Command reference**.                                               |
| **Idempotent reads**                      | `safe info`, `safe balances`, `safe nonce`, `tx list`, and `tx show` are safe to retry or replay arbitrarily.                                                                                   |
| **Refusal semantics**                     | `tx sign` refuses to double-sign, re-sign an executed tx, or sign past the threshold. Agents can rely on the CLI to reject obviously wrong actions instead of encoding those checks themselves. |
| **Built-in discovery**                    | `lem --help`, `lem <command> --help`, and `lem <command> <subcommand> --help` produce stable, parseable help. Many agent frameworks can derive a tool schema from this output.                  |
| **Deterministic signers for CI**          | `--salt + --index` derives a stable test signer from a single secret. There is no need to plumb a mnemonic through your agent runtime.                                                          |

### Step-by-step

#### 1. Pair a non-interactive signer

For agents that operate on testnets and CI, derive a deterministic signer from a salt held in your secret manager:

```
lem connect --salt "$AGENT_SALT" --index 0
```

For agents that propose on mainnet, **keep a human in the loop**: the agent prepares the transaction, but a human approves on a Ledger device. See the security note at the bottom of this page.

#### 2. Set up env (only if self-hosting endpoints)

If your agent runtime uses Ledger's hosted Transaction Service and RPCs, skip this step. The distributed binary already knows where to call.

If you point `lem` at your own endpoints, export the env vars from the **Command reference** before running commands, or place them in a file passed via `--env`.

#### 3. Call `lem` from a tool-use loop

Build the calling convention into your agent's tool schema. For each `lem` command, define an input schema (the flags) and let the agent unmarshal stdout as JSON. A minimal contract:

* Run the command.
* On exit code 0, parse stdout as JSON and return to the model.
* On non-zero exit code, parse stdout as `{ error: { code, message, suggestion } }` and return that. Many agents will then retry with a corrected call, for example by running `lem connect` after a `CONFIG_ERROR`.

### Recipe: Bash agent harness

Propose a payment, wait for the second signature, and execute, all from one script. Useful as a reference for shaping the tool-call schemas the agent will use.

```
#!/usr/bin/env bash
set -euo pipefail

SAFE="0xYourSafeAddress"
CHAIN_ID="11155111"
TO="0xRecipientAddress"
VALUE="100000000000000"

# 1. Make sure a signer is paired.
if ! lem config show > /dev/null 2>&1; then
  lem connect --salt "$AGENT_SALT" --index 0
fi

# 2. Sanity-check that the signer owns the Safe.
lem safe info --address "$SAFE" --chain-id "$CHAIN_ID" \
  | jq -e --arg signer "$(lem config show | jq -r '.config.address')" \
    '.owners | map(ascii_downcase) | index($signer | ascii_downcase) != null' > /dev/null

# 3. Propose.
PROPOSAL=$(lem tx propose \
  --address "$SAFE" --chain-id "$CHAIN_ID" \
  --to "$TO" --value "$VALUE")
SAFE_TX_HASH=$(echo "$PROPOSAL" | jq -r '.safeTxHash')

# 4. Wait for the threshold to be met (a co-owner signs out of band).
while true; do
  TX=$(lem tx show --safe-tx-hash "$SAFE_TX_HASH")
  HAVE=$(echo "$TX" | jq '.confirmations | length')
  NEED=$(echo "$TX" | jq '.confirmationsRequired')
  if [ "$HAVE" -ge "$NEED" ]; then break; fi
  sleep 30
done

# 5. Execute.
lem tx execute --chain-id "$CHAIN_ID" "$SAFE_TX_HASH"
```

Every line is a JSON-in / JSON-out call that an LLM agent can substitute for `jq` plumbing.

### Recipe: Python agent tool

A minimal Python wrapper that an LLM tool-use agent (LangChain, OpenAI tools, Anthropic tools, etc.) can register as a single tool with multiple subcommands.

```
import json
import subprocess

class LemError(Exception):
    def __init__(self, code: str, message: str, suggestion: str | None = None):
        self.code = code
        self.message = message
        self.suggestion = suggestion
        super().__init__(f"{code}: {message}")

def call_lem(args: list[str]) -> dict:
    """Invoke `lem` and return parsed stdout, or raise LemError on failure."""
    result = subprocess.run(["lem", *args], capture_output=True, text=True)
    payload = json.loads(result.stdout) if result.stdout.strip() else {}
    if result.returncode != 0:
        err = payload.get("error", {})
        raise LemError(
            code=err.get("code", "UNKNOWN"),
            message=err.get("message", result.stderr.strip() or "lem failed"),
            suggestion=err.get("suggestion"),
        )
    return payload

# Example: propose an ETH transfer.
proposal = call_lem([
    "tx", "propose",
    "--address", "0xYourSafeAddress",
    "--chain-id", "11155111",
    "--to", "0xRecipientAddress",
    "--value", "100000000000000",
])
print(proposal["safeTxHash"])
```

For agent tool-use, expose `call_lem` as a single tool whose argument is the array of CLI args. The model can then form arbitrary `lem` invocations and recover gracefully from errors using the code and suggestion fields.

### Exposing `lem` over MCP

The Model Context Protocol (MCP) lets agents discover and invoke external tools. To expose `lem` to an MCP-aware host:

1. Write a thin MCP server (one tool per `lem` subcommand, or a single "run `lem`" tool that accepts an args array. Both work).
2. Map each tool's input schema to the flags documented in **Command reference**.
3. Forward stdout JSON as the tool's success result. Forward the error object as the failure result.
4. Run the MCP server alongside your agent. The host (Claude Desktop, Cursor, etc.) takes care of routing tool-calls.

The minimal-effort version is the "one tool with an args array" approach in the Python recipe above, wrapped in your MCP framework of choice. The more polished version is one MCP tool per `lem` subcommand, with input schemas derived from the flag tables in **Command reference**.

### Security

* **Never give an agent direct access to a mainnet mnemonic.** `--seed` is a footgun outside of testnets. For mainnet, the agent should *propose* transactions with `lem tx propose` against a Safe whose owners are real Ledger devices. Execution and the on-chain signature stay with humans.
* **Treat** `--salt` as a secret. Anyone with the salt + index can sign as that signer. Store it in your secret manager, not in source.
* **Scope agent capabilities.** Even on testnets, you can run agents under a signer that is only an owner of low-value Safes, so an agent gone rogue cannot drain anything important.
* **Audit the trail.** Every proposal made by the CLI carries `origin: { app: "les-multisig-cli" }` in the Transaction Service, so you can filter agent-originated transactions in the Ledger Multisig UI.

### Next steps

* **Command reference**
* [Guides](https://help.multisig.ledger.com/guides)


# Reference

The Ledger Multisig Transaction Service API provides a comprehensive suite of endpoints designed to orchestrate the lifecycle of multi-signature transactions. It serves as the backend infrastructure for tracking Safe accounts, managing pending signatures, and broadcasting fully executed transactions.

This reference documentation details every available endpoint, enabling you to build custom integrations, automate treasury flows, or develop interface extensions on top of the Ledger Multisig ecosystem.

#### Supported Networks & Endpoints

The Ledger Multisig API is deployed across multiple EVM chains. While the endpoint structure remains consistent, you must target the specific `chain_id` or base URL corresponding to your desired network.

Use the selector below to identify the correct Chain ID for your requests:

| Supported Network          | Chain ID  |
| -------------------------- | --------- |
| Ethereum Mainnet           | 1n        |
| Base                       | 8453n     |
| Arbitrum                   | 42161n    |
| Polygon                    | 137n      |
| Optimism                   | 10n       |
| Ethereum Sepolia (testnet) | 11155111n |


# Delegates

## GET /api/v2/delegates/

> Returns a list with all the delegates

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeDelegateResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeDelegateResponse"}}}},"SafeDelegateResponse":{"type":"object","properties":{"safe":{"type":"string"},"delegate":{"type":"string"},"delegator":{"type":"string"},"label":{"type":"string","maxLength":50},"expiryDate":{"type":"string","format":"date-time"}},"required":["delegate","delegator","expiryDate","label","safe"]}}},"paths":{"/api/v2/delegates/":{"get":{"operationId":"delegates_list_2","description":"Returns a list with all the delegates","parameters":[{"in":"query","name":"safe","schema":{"type":["string","null"]}},{"in":"query","name":"delegate","schema":{"type":"string"}},{"in":"query","name":"delegator","schema":{"type":"string"}},{"in":"query","name":"label","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of results to return per page.","schema":{"type":"integer"}},{"name":"offset","required":false,"in":"query","description":"The initial index from which to return the results.","schema":{"type":"integer"}}],"tags":["delegates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeDelegateResponseList"}}},"description":""},"400":{"description":"Invalid data"}}}}}}
```

## POST /api/v2/delegates/

> Adds a new Safe delegate with a custom label. Calls with same delegate but different label or\
> signer will update the label or delegator if a different one is provided.\
> To generate the signature, the following EIP712 data hash needs to be signed:\
> \
> \`\`\`python\
> &#x20;{\
> &#x20;   "types": {\
> &#x20;       "EIP712Domain": \[\
> &#x20;           {"name": "name", "type": "string"},\
> &#x20;           {"name": "version", "type": "string"},\
> &#x20;           {"name": "chainId", "type": "uint256"},\
> &#x20;       ],\
> &#x20;       "Delegate": \[\
> &#x20;           {"name": "delegateAddress", "type": "address"},\
> &#x20;           {"name": "totp", "type": "uint256"},\
> &#x20;       ],\
> &#x20;   },\
> &#x20;   "primaryType": "Delegate",\
> &#x20;   "domain": {\
> &#x20;       "name": "Safe Transaction Service",\
> &#x20;       "version": "1.0",\
> &#x20;       "chainId": chain\_id,\
> &#x20;   },\
> &#x20;   "message": {\
> &#x20;       "delegateAddress": delegate\_address,\
> &#x20;       "totp": totp,\
> &#x20;   },\
> }\
> \`\`\`\
> \
> For the signature we use \`TOTP\` with \`T0=0\` and \`Tx=3600\`. \`TOTP\` is calculated by taking the\
> Unix UTC epoch time (no milliseconds) and dividing by 3600 (natural division, no decimals).

````json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"DelegateSerializerV2":{"type":"object","description":"Mixin to validate delegate operations data","properties":{"safe":{"type":["string","null"]},"delegate":{"type":"string"},"delegator":{"type":"string"},"signature":{"type":"string"},"label":{"type":"string","maxLength":50},"expiryDate":{"type":["string","null"],"format":"date-time"}},"required":["delegate","delegator","label","signature"]}}},"paths":{"/api/v2/delegates/":{"post":{"operationId":"delegates_create_2","description":"Adds a new Safe delegate with a custom label. Calls with same delegate but different label or\nsigner will update the label or delegator if a different one is provided.\nTo generate the signature, the following EIP712 data hash needs to be signed:\n\n```python\n {\n    \"types\": {\n        \"EIP712Domain\": [\n            {\"name\": \"name\", \"type\": \"string\"},\n            {\"name\": \"version\", \"type\": \"string\"},\n            {\"name\": \"chainId\", \"type\": \"uint256\"},\n        ],\n        \"Delegate\": [\n            {\"name\": \"delegateAddress\", \"type\": \"address\"},\n            {\"name\": \"totp\", \"type\": \"uint256\"},\n        ],\n    },\n    \"primaryType\": \"Delegate\",\n    \"domain\": {\n        \"name\": \"Safe Transaction Service\",\n        \"version\": \"1.0\",\n        \"chainId\": chain_id,\n    },\n    \"message\": {\n        \"delegateAddress\": delegate_address,\n        \"totp\": totp,\n    },\n}\n```\n\nFor the signature we use `TOTP` with `T0=0` and `Tx=3600`. `TOTP` is calculated by taking the\nUnix UTC epoch time (no milliseconds) and dividing by 3600 (natural division, no decimals).","tags":["delegates"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DelegateSerializerV2"}}},"required":true},"responses":{"202":{"description":"Accepted"},"400":{"description":"Malformed data"}}}}}}
````

## DELETE /api/v2/delegates/{delegate\_address}/

> Removes every delegate/delegator pair found associated with a given delegate address. The\
> signature is built the same way as for adding a delegate, but in this case the signer can be\
> either the \`delegator\` (owner) or the \`delegate\` itself. Check \`POST /delegates/\` to learn more.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}}},"paths":{"/api/v2/delegates/{delegate_address}/":{"delete":{"operationId":"delegates_destroy_2","description":"Removes every delegate/delegator pair found associated with a given delegate address. The\nsignature is built the same way as for adding a delegate, but in this case the signer can be\neither the `delegator` (owner) or the `delegate` itself. Check `POST /delegates/` to learn more.","parameters":[{"in":"path","name":"delegate_address","schema":{"type":"string"},"required":true}],"tags":["delegates"],"responses":{"204":{"description":"Deleted"},"400":{"description":"Malformed data"},"404":{"description":"Delegate not found"},"422":{"description":"Invalid Ethereum address/Error processing data"}}}}}}
```


# Transactions

## GET /api/v2/multisig-transactions/{safe\_tx\_hash}/

> Returns a multi-signature transaction given its Safe transaction hash

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeMultisigTransactionResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","trusted","value"]}}},"paths":{"/api/v2/multisig-transactions/{safe_tx_hash}/":{"get":{"operationId":"multisig_transactions_retrieve_2","description":"Returns a multi-signature transaction given its Safe transaction hash","parameters":[{"in":"path","name":"safe_tx_hash","schema":{"type":"string"},"required":true}],"tags":["transactions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeMultisigTransactionResponseSerializerV2"}}},"description":""}}}}}}
```

## DELETE /api/v2/multisig-transactions/{safe\_tx\_hash}/

> Removes the queued but not executed multi-signature transaction associated with the given Safe transaction hash.\
> Only the proposer or the delegate who proposed the transaction can delete it.\
> If the transaction was proposed by a delegate, it must still be a valid delegate for the transaction proposer.\
> An EOA is required to sign the following EIP-712 data:\
> \
> \`\`\`python\
> &#x20;{\
> &#x20;   "types": {\
> &#x20;       "EIP712Domain": \[\
> &#x20;           {"name": "name", "type": "string"},\
> &#x20;           {"name": "version", "type": "string"},\
> &#x20;           {"name": "chainId", "type": "uint256"},\
> &#x20;           {"name": "verifyingContract", "type": "address"},\
> &#x20;       ],\
> &#x20;       "DeleteRequest": \[\
> &#x20;           {"name": "safeTxHash", "type": "bytes32"},\
> &#x20;           {"name": "totp", "type": "uint256"},\
> &#x20;       ],\
> &#x20;   },\
> &#x20;   "primaryType": "DeleteRequest",\
> &#x20;   "domain": {\
> &#x20;       "name": "Safe Transaction Service",\
> &#x20;       "version": "1.0",\
> &#x20;       "chainId": chain\_id,\
> &#x20;       "verifyingContract": safe\_address,\
> &#x20;   },\
> &#x20;   "message": {\
> &#x20;       "safeTxHash": safe\_tx\_hash,\
> &#x20;       "totp": totp,\
> &#x20;   },\
> }\
> \`\`\`\
> \
> \`totp\` parameter is calculated with \`T0=0\` and \`Tx=3600\`. \`totp\` is calculated by taking the\
> Unix UTC epoch time (no milliseconds) and dividing by 3600 (natural division, no decimals)

````json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}}},"paths":{"/api/v2/multisig-transactions/{safe_tx_hash}/":{"delete":{"operationId":"multisig_transactions_destroy_2","description":"Removes the queued but not executed multi-signature transaction associated with the given Safe transaction hash.\nOnly the proposer or the delegate who proposed the transaction can delete it.\nIf the transaction was proposed by a delegate, it must still be a valid delegate for the transaction proposer.\nAn EOA is required to sign the following EIP-712 data:\n\n```python\n {\n    \"types\": {\n        \"EIP712Domain\": [\n            {\"name\": \"name\", \"type\": \"string\"},\n            {\"name\": \"version\", \"type\": \"string\"},\n            {\"name\": \"chainId\", \"type\": \"uint256\"},\n            {\"name\": \"verifyingContract\", \"type\": \"address\"},\n        ],\n        \"DeleteRequest\": [\n            {\"name\": \"safeTxHash\", \"type\": \"bytes32\"},\n            {\"name\": \"totp\", \"type\": \"uint256\"},\n        ],\n    },\n    \"primaryType\": \"DeleteRequest\",\n    \"domain\": {\n        \"name\": \"Safe Transaction Service\",\n        \"version\": \"1.0\",\n        \"chainId\": chain_id,\n        \"verifyingContract\": safe_address,\n    },\n    \"message\": {\n        \"safeTxHash\": safe_tx_hash,\n        \"totp\": totp,\n    },\n}\n```\n\n`totp` parameter is calculated with `T0=0` and `Tx=3600`. `totp` is calculated by taking the\nUnix UTC epoch time (no milliseconds) and dividing by 3600 (natural division, no decimals)","parameters":[{"in":"path","name":"safe_tx_hash","schema":{"type":"string"},"required":true}],"tags":["transactions"],"responses":{"204":{"description":"No response body"}}}}}}
````

## GET /api/v2/safes/{address}/all-transactions/

> Returns all the \*executed\* transactions for a given Safe address.\
> The list has different structures depending on the transaction type:\
> \- Multisig Transactions for a Safe. \`tx\_type=MULTISIG\_TRANSACTION\`.\
> \- Module Transactions for a Safe. \`tx\_type=MODULE\_TRANSACTION\`\
> \- Incoming Transfers of Ether/ERC20 Tokens/ERC721 Tokens. \`tx\_type=ETHEREUM\_TRANSACTION\`\
> Ordering\_fields: \["timestamp"] eg: \`-timestamp\` (default one) or \`timestamp\`\
> \
> Note: This endpoint has a bug that will be fixed in next versions of the endpoint. Pagination is done\
> using the \`Transaction Hash\`, and due to that the number of relevant transactions with the same\
> \`Transaction Hash\` cannot be known beforehand. So if there are only 2 transactions\
> with the same \`Transaction Hash\`, \`count\` of the endpoint will be 1\
> but there will be 2 transactions in the list.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedAllTransactionsSchemaSerializerV2List":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/AllTransactionsSchemaSerializerV2"}}}},"AllTransactionsSchemaSerializerV2":{"type":"object","description":"Just for the purpose of documenting, don't use it","properties":{"txType1":{"$ref":"#/components/schemas/SafeModuleTransactionWithTransfersResponse"},"txType2":{"$ref":"#/components/schemas/SafeMultisigTransactionWithTransfersResponseSerializerV2"},"txType3":{"$ref":"#/components/schemas/EthereumTxWithTransfersResponse"}},"required":["txType1","txType2","txType3"]},"SafeModuleTransactionWithTransfersResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","transfers","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]},"SafeMultisigTransactionWithTransfersResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"EthereumTxWithTransfersResponse":{"type":"object","properties":{"executionDate":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"data":{"type":"string"},"txHash":{"type":"string"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true},"from":{"type":"string"}},"required":["blockNumber","data","executionDate","from","to","transfers","txHash","txType"]}}},"paths":{"/api/v2/safes/{address}/all-transactions/":{"get":{"operationId":"safes_all_transactions_list_2","description":"Returns all the *executed* transactions for a given Safe address.\nThe list has different structures depending on the transaction type:\n- Multisig Transactions for a Safe. `tx_type=MULTISIG_TRANSACTION`.\n- Module Transactions for a Safe. `tx_type=MODULE_TRANSACTION`\n- Incoming Transfers of Ether/ERC20 Tokens/ERC721 Tokens. `tx_type=ETHEREUM_TRANSACTION`\nOrdering_fields: [\"timestamp\"] eg: `-timestamp` (default one) or `timestamp`\n\nNote: This endpoint has a bug that will be fixed in next versions of the endpoint. Pagination is done\nusing the `Transaction Hash`, and due to that the number of relevant transactions with the same\n`Transaction Hash` cannot be known beforehand. So if there are only 2 transactions\nwith the same `Transaction Hash`, `count` of the endpoint will be 1\nbut there will be 2 transactions in the list.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"name":"ordering","required":false,"in":"query","description":"Which field to use when ordering the results.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of results to return per page.","schema":{"type":"integer"}},{"name":"offset","required":false,"in":"query","description":"The initial index from which to return the results.","schema":{"type":"integer"}}],"tags":["transactions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedAllTransactionsSchemaSerializerV2List"}}},"description":""}}}}}}
```

## GET /api/v2/safes/{address}/multisig-transactions/

> Returns all the multi-signature transactions for a given Safe address.\
> By default, only \`\`trusted\`\` multisig transactions are returned.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeMultisigTransactionResponseSerializerV2List":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeMultisigTransactionResponseSerializerV2"}}}},"SafeMultisigTransactionResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","trusted","value"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/api/v2/safes/{address}/multisig-transactions/":{"get":{"operationId":"safes_multisig_transactions_list_2","description":"Returns all the multi-signature transactions for a given Safe address.\nBy default, only ``trusted`` multisig transactions are returned.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"in":"query","name":"failed","schema":{"type":"boolean"}},{"in":"query","name":"modified__lt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"modified__gt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"modified__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"modified__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"nonce__lt","schema":{"type":"number"}},{"in":"query","name":"nonce__gt","schema":{"type":"number"}},{"in":"query","name":"nonce__lte","schema":{"type":"number"}},{"in":"query","name":"nonce__gte","schema":{"type":"number"}},{"in":"query","name":"nonce","schema":{"type":"number"}},{"in":"query","name":"safe_tx_hash","schema":{"type":"string","format":"byte"}},{"in":"query","name":"to","schema":{"type":"string"}},{"in":"query","name":"value__lt","schema":{"type":"number"}},{"in":"query","name":"value__gt","schema":{"type":"number"}},{"in":"query","name":"value","schema":{"type":"number"}},{"in":"query","name":"executed","schema":{"type":"boolean"}},{"in":"query","name":"has_confirmations","schema":{"type":"boolean"}},{"in":"query","name":"trusted","schema":{"type":"boolean"}},{"in":"query","name":"execution_date__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"execution_date__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"submission_date__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"submission_date__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"transaction_hash","schema":{"type":["string","null"],"format":"byte"}},{"name":"ordering","required":false,"in":"query","description":"Which field to use when ordering the results.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of results to return per page.","schema":{"type":"integer"}},{"name":"offset","required":false,"in":"query","description":"The initial index from which to return the results.","schema":{"type":"integer"}}],"tags":["transactions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeMultisigTransactionResponseSerializerV2List"}}},"description":""},"400":{"description":"Invalid data"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Invalid ethereum address"}}}}}}
```

## POST /api/v2/safes/{address}/multisig-transactions/

> Creates a multi-signature transaction for a given Safe account with its confirmations and\
> retrieves all the information related.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeMultisigTransaction":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"integer","minimum":0},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"integer","minimum":0},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"contractTransactionHash":{"type":"string"},"sender":{"type":"string"},"signature":{"type":["string","null"]},"origin":{"type":["string","null"],"maxLength":200}},"required":["baseGas","contractTransactionHash","gasPrice","nonce","operation","safe","safeTxGas","sender","to","value"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/api/v2/safes/{address}/multisig-transactions/":{"post":{"operationId":"safes_multisig_transactions_create_2","description":"Creates a multi-signature transaction for a given Safe account with its confirmations and\nretrieves all the information related.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true}],"tags":["transactions"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeMultisigTransaction"}}},"required":true},"responses":{"201":{"description":"Created or signature updated"},"400":{"description":"Invalid data"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Invalid ethereum address | User is not an owner | Invalid safeTxHash |Invalid signature | Nonce already executed | Sender is not an owner"}}}}}}
```


# Safes

## GET /api/v2/safes/{address}/balances/

> Get paginated balances for Ether and ERC20 tokens.\
> The maximum limit allowed is 200.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeCollectibleResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeCollectibleResponse"}}}},"SafeCollectibleResponse":{"type":"object","properties":{"address":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"logoUri":{"type":"string"},"id":{"type":"string"},"uri":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"imageUri":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["address","description","id","imageUri","logoUri","metadata","name","tokenName","tokenSymbol","uri"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/api/v2/safes/{address}/balances/":{"get":{"operationId":"safes_balances_retrieve_2","description":"Get paginated balances for Ether and ERC20 tokens.\nThe maximum limit allowed is 200.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"in":"query","name":"trusted","schema":{"type":"boolean","default":false},"description":"If `True` just trusted tokens will be returned"},{"in":"query","name":"exclude_spam","schema":{"type":"boolean","default":false},"description":"If `True` spam tokens will not be returned"},{"in":"query","name":"limit","schema":{"type":"integer"},"description":"Number of results to return per page."},{"in":"query","name":"offset","schema":{"type":"integer"},"description":"The initial index from which to return the results."}],"tags":["safes"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeCollectibleResponseList"}}},"description":""},"404":{"description":"Safe not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Safe address checksum not valid"}}}}}}
```

## GET /api/v2/safes/{address}/collectibles/

> Get paginated collectibles (ERC721 tokens) and information about them of a given Safe account.\
> The maximum limit allowed is 10.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeCollectibleResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeCollectibleResponse"}}}},"SafeCollectibleResponse":{"type":"object","properties":{"address":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"logoUri":{"type":"string"},"id":{"type":"string"},"uri":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"imageUri":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["address","description","id","imageUri","logoUri","metadata","name","tokenName","tokenSymbol","uri"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/api/v2/safes/{address}/collectibles/":{"get":{"operationId":"safes_collectibles_retrieve","description":"Get paginated collectibles (ERC721 tokens) and information about them of a given Safe account.\nThe maximum limit allowed is 10.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"in":"query","name":"trusted","schema":{"type":"boolean","default":false},"description":"If `True` just trusted tokens will be returned"},{"in":"query","name":"exclude_spam","schema":{"type":"boolean","default":false},"description":"If `True` spam tokens will not be returned"},{"in":"query","name":"limit","schema":{"type":"integer"},"description":"Number of results to return per page."},{"in":"query","name":"offset","schema":{"type":"integer"},"description":"The initial index from which to return the results."}],"tags":["safes"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeCollectibleResponseList"}}},"description":""},"404":{"description":"Safe not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Safe address checksum not valid"}}}}}}
```

## GET /tx-service/eth/api/v1/safes/{address}/

> Returns detailed information of a given Safe account

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeInfoResponse":{"type":"object","properties":{"address":{"type":"string"},"nonce":{"type":"string"},"threshold":{"type":"integer"},"owners":{"type":"array","items":{"type":"string"}},"masterCopy":{"type":"string"},"modules":{"type":"array","items":{"type":"string"}},"fallbackHandler":{"type":"string"},"guard":{"type":"string"},"version":{"type":["string","null"]}},"required":["address","fallbackHandler","guard","masterCopy","modules","nonce","owners","threshold","version"]}}},"paths":{"/tx-service/eth/api/v1/safes/{address}/":{"get":{"operationId":"safes_retrieve","description":"Returns detailed information of a given Safe account","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true}],"tags":["safes"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeInfoResponse"}}},"description":""},"404":{"description":"Safe not found"},"422":{"description":"code = 1: Checksum address validation failed\ncode = 50: Cannot get Safe info"}}}}}}
```

## GET /tx-service/eth/api/v1/safes/{address}/creation/

> Returns detailed information on the Safe creation transaction of a given Safe.\
> \
> Note: When event indexing is being used and multiple Safes are deployed in the same transaction\
> the result might not be accurate due to the indexer not knowing which events belong to which Safe\
> deployment.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeCreationInfoResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"creator":{"type":"string"},"transactionHash":{"type":"string"},"factoryAddress":{"type":"string"},"masterCopy":{"type":["string","null"]},"setupData":{"type":["string","null"]},"saltNonce":{"type":["string","null"]},"dataDecoded":{"type":"object","additionalProperties":{},"readOnly":true},"userOperation":{"oneOf":[{"$ref":"#/components/schemas/UserOperationWithSafeOperationResponse"},{"type":"null"}]}},"required":["created","creator","dataDecoded","factoryAddress","masterCopy","saltNonce","setupData","transactionHash","userOperation"]},"UserOperationWithSafeOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"safeOperation":{"oneOf":[{"$ref":"#/components/schemas/SafeOperationResponse"},{"type":"null"}],"readOnly":true}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","safeOperation","sender","signature","userOperationHash","verificationGasLimit"]},"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}},"paths":{"/tx-service/eth/api/v1/safes/{address}/creation/":{"get":{"operationId":"safes_creation_retrieve","description":"Returns detailed information on the Safe creation transaction of a given Safe.\n\nNote: When event indexing is being used and multiple Safes are deployed in the same transaction\nthe result might not be accurate due to the indexer not knowing which events belong to which Safe\ndeployment.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true}],"tags":["safes"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeCreationInfoResponse"}}},"description":""},"404":{"description":"Safe creation not found"},"422":{"description":"Owner address checksum not valid"},"503":{"description":"Problem connecting to Ethereum network"}}}}}}
```

## GET /tx-service/eth/api/v1/safes/{address}/export/

> Get transactions optimized for CSV export with transfer information.\
> The maximum limit allowed is 1000.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeExportTransactionList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeExportTransaction"}}}},"SafeExportTransaction":{"type":"object","description":"Serializer for the export endpoint that returns transaction data optimized for CSV export","properties":{"safe":{"type":"string"},"From":{"type":"string","title":" from"},"to":{"type":"string"},"amount":{"type":"string"},"assetType":{"type":"string"},"assetAddress":{"type":["string","null"]},"assetSymbol":{"type":["string","null"]},"assetDecimals":{"type":["integer","null"]},"proposerAddress":{"type":["string","null"]},"proposedAt":{"type":["string","null"],"format":"date-time"},"executorAddress":{"type":["string","null"]},"executedAt":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"]},"transactionHash":{"type":"string"},"contractAddress":{"type":["string","null"]},"nonce":{"type":["string","null"]}},"required":["From","amount","assetAddress","assetDecimals","assetSymbol","assetType","contractAddress","executedAt","executorAddress","nonce","note","proposedAt","proposerAddress","safe","to","transactionHash"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/tx-service/eth/api/v1/safes/{address}/export/":{"get":{"operationId":"safes_export_retrieve","description":"Get transactions optimized for CSV export with transfer information.\nThe maximum limit allowed is 1000.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"in":"query","name":"execution_date__gte","schema":{"type":"string","format":"date-time"},"description":"Filter transactions executed after this date (ISO format)"},{"in":"query","name":"execution_date__lte","schema":{"type":"string","format":"date-time"},"description":"Filter transactions executed before this date (ISO format)"},{"in":"query","name":"limit","schema":{"type":"integer"},"description":"Maximum number of transactions to return (max 1000)"},{"in":"query","name":"offset","schema":{"type":"integer"},"description":"Number of transactions to skip"}],"tags":["safes"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeExportTransactionList"}}},"description":""},"404":{"description":"Safe not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Safe address checksum not valid"}}}}}}
```

## GET /tx-service/eth/api/v2/safes/{address}/balances/

> Get paginated balances for Ether and ERC20 tokens.\
> The maximum limit allowed is 200.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeCollectibleResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeCollectibleResponse"}}}},"SafeCollectibleResponse":{"type":"object","properties":{"address":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"logoUri":{"type":"string"},"id":{"type":"string"},"uri":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"imageUri":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["address","description","id","imageUri","logoUri","metadata","name","tokenName","tokenSymbol","uri"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/tx-service/eth/api/v2/safes/{address}/balances/":{"get":{"operationId":"safes_balances_retrieve_2","description":"Get paginated balances for Ether and ERC20 tokens.\nThe maximum limit allowed is 200.","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"in":"query","name":"trusted","schema":{"type":"boolean","default":false},"description":"If `True` just trusted tokens will be returned"},{"in":"query","name":"exclude_spam","schema":{"type":"boolean","default":false},"description":"If `True` spam tokens will not be returned"},{"in":"query","name":"limit","schema":{"type":"integer"},"description":"Number of results to return per page."},{"in":"query","name":"offset","schema":{"type":"integer"},"description":"The initial index from which to return the results."}],"tags":["safes"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeCollectibleResponseList"}}},"description":""},"404":{"description":"Safe not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Safe address checksum not valid"}}}}}}
```


# Modules

## GET /tx-service/eth/api/v1/modules/{address}/safes/

> Returns the list of Safes that have the provided module enabled

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"ModulesResponse":{"type":"object","properties":{"safes":{"type":"array","items":{"type":"string"}}},"required":["safes"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/tx-service/eth/api/v1/modules/{address}/safes/":{"get":{"operationId":"modules_safes_retrieve","description":"Returns the list of Safes that have the provided module enabled","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true}],"tags":["modules"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModulesResponse"}}},"description":""},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Module address checksum not valid"}}}}}}
```


# Owners

## GET /tx-service/eth/api/v1/owners/{address}/safes/

> Returns the list of Safe accounts that have the given address as their owner

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"OwnerResponse":{"type":"object","properties":{"safes":{"type":"array","items":{"type":"string"}}},"required":["safes"]},"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}},"paths":{"/tx-service/eth/api/v1/owners/{address}/safes/":{"get":{"operationId":"owners_safes_retrieve","description":"Returns the list of Safe accounts that have the given address as their owner","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true}],"tags":["owners"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerResponse"}}},"description":""},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorResponse"}}},"description":"Owner address checksum not valid"}}}}}}
```


# 4337

## GET /tx-service/eth/api/v1/safe-operations/{safe\_operation\_hash}/

> Returns a SafeOperation given its Safe operation hash

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeOperationWithUserOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true},"userOperation":{"allOf":[{"$ref":"#/components/schemas/UserOperationResponse"}],"readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","userOperation","validAfter","validUntil"]},"UserOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","sender","signature","userOperationHash","verificationGasLimit"]}}},"paths":{"/tx-service/eth/api/v1/safe-operations/{safe_operation_hash}/":{"get":{"operationId":"safe_operations_retrieve","description":"Returns a SafeOperation given its Safe operation hash","parameters":[{"in":"path","name":"safe_operation_hash","schema":{"type":"string"},"required":true}],"tags":["4337"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeOperationWithUserOperationResponse"}}},"description":""}}}}}}
```

## GET /tx-service/eth/api/v1/safe-operations/{safe\_operation\_hash}/confirmations/

> Get the list of confirmations for a multisig transaction

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeOperationConfirmationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeOperationConfirmationResponse"}}}},"SafeOperationConfirmationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"owner":{"type":"string"},"signature":{"type":"string"},"signatureType":{"type":"string","readOnly":true}},"required":["created","modified","owner","signature","signatureType"]}}},"paths":{"/tx-service/eth/api/v1/safe-operations/{safe_operation_hash}/confirmations/":{"get":{"operationId":"safe_operations_confirmations_list","description":"Get the list of confirmations for a multisig transaction","parameters":[{"in":"path","name":"safe_operation_hash","schema":{"type":"string"},"required":true},{"name":"limit","required":false,"in":"query","description":"Number of results to return per page.","schema":{"type":"integer"}},{"name":"offset","required":false,"in":"query","description":"The initial index from which to return the results.","schema":{"type":"integer"}}],"tags":["4337"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeOperationConfirmationResponseList"}}},"description":""},"400":{"description":"Invalid data"}}}}}}
```

## POST /tx-service/eth/api/v1/safe-operations/{safe\_operation\_hash}/confirmations/

> Add a confirmation for a transaction. More than one signature can be used. This endpoint does not support\
> the use of delegates to make a transaction trusted.

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeOperationConfirmation":{"type":"object","description":"Validate new confirmations for an existing `SafeOperation`","properties":{"signature":{"type":"string"}},"required":["signature"]}}},"paths":{"/tx-service/eth/api/v1/safe-operations/{safe_operation_hash}/confirmations/":{"post":{"operationId":"safe_operations_confirmations_create","description":"Add a confirmation for a transaction. More than one signature can be used. This endpoint does not support\nthe use of delegates to make a transaction trusted.","parameters":[{"in":"path","name":"safe_operation_hash","schema":{"type":"string"},"required":true}],"tags":["4337"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeOperationConfirmation"}}},"required":true},"responses":{"201":{"description":"Created"},"400":{"description":"Malformed data"},"422":{"description":"Error processing data"}}}}}}
```

## GET /tx-service/eth/api/v1/safes/{address}/safe-operations/

> Returns the list of SafeOperations for a given Safe account

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedSafeOperationWithUserOperationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeOperationWithUserOperationResponse"}}}},"SafeOperationWithUserOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true},"userOperation":{"allOf":[{"$ref":"#/components/schemas/UserOperationResponse"}],"readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","userOperation","validAfter","validUntil"]},"UserOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","sender","signature","userOperationHash","verificationGasLimit"]}}},"paths":{"/tx-service/eth/api/v1/safes/{address}/safe-operations/":{"get":{"operationId":"safes_safe_operations_list","description":"Returns the list of SafeOperations for a given Safe account","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"in":"query","name":"modified__lt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"modified__gt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"modified__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"modified__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_after__lt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_after__gt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_after__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_after__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_until__lt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_until__gt","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_until__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"valid_until__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"module_address","schema":{"type":"string"}},{"in":"query","name":"executed","schema":{"type":"boolean"}},{"in":"query","name":"has_confirmations","schema":{"type":"boolean"}},{"in":"query","name":"execution_date__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"execution_date__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"submission_date__gte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"submission_date__lte","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"transaction_hash","schema":{"type":["string","null"],"format":"byte"}},{"name":"ordering","required":false,"in":"query","description":"Which field to use when ordering the results.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of results to return per page.","schema":{"type":"integer"}},{"name":"offset","required":false,"in":"query","description":"The initial index from which to return the results.","schema":{"type":"integer"}}],"tags":["4337"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSafeOperationWithUserOperationResponseList"}}},"description":""}}}}}}
```

## POST /tx-service/eth/api/v1/safes/{address}/safe-operations/

> Adds a new SafeOperation for a given Safe account

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"SafeOperation":{"type":"object","description":"Mixin class to validate SafeOperation signatures. `_get_owners` can be overridden to define\nthe valid owners to sign","properties":{"nonce":{"type":"integer","minimum":0},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"integer","minimum":0},"verificationGasLimit":{"type":"integer","minimum":0},"preVerificationGas":{"type":"integer","minimum":0},"maxFeePerGas":{"type":"integer","minimum":0},"maxPriorityFeePerGas":{"type":"integer","minimum":0},"paymasterAndData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"validAfter":{"type":["string","null"],"format":"date-time"},"validUntil":{"type":["string","null"],"format":"date-time"},"moduleAddress":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","initCode","maxFeePerGas","maxPriorityFeePerGas","moduleAddress","nonce","paymasterAndData","preVerificationGas","signature","validAfter","validUntil","verificationGasLimit"]}}},"paths":{"/tx-service/eth/api/v1/safes/{address}/safe-operations/":{"post":{"operationId":"safes_safe_operations_create","description":"Adds a new SafeOperation for a given Safe account","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true}],"tags":["4337"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SafeOperation"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{},"description":"Unspecified response body"}}},"description":""}}}}}}
```

## GET /tx-service/eth/api/v1/safes/{address}/user-operations/

> Returns the list of UserOperations for a given Safe account

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"PaginatedUserOperationWithSafeOperationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/UserOperationWithSafeOperationResponse"}}}},"UserOperationWithSafeOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"safeOperation":{"oneOf":[{"$ref":"#/components/schemas/SafeOperationResponse"},{"type":"null"}],"readOnly":true}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","safeOperation","sender","signature","userOperationHash","verificationGasLimit"]},"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}},"paths":{"/tx-service/eth/api/v1/safes/{address}/user-operations/":{"get":{"operationId":"safes_user_operations_list","description":"Returns the list of UserOperations for a given Safe account","parameters":[{"in":"path","name":"address","schema":{"type":"string"},"required":true},{"name":"ordering","required":false,"in":"query","description":"Which field to use when ordering the results.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of results to return per page.","schema":{"type":"integer"}},{"name":"offset","required":false,"in":"query","description":"The initial index from which to return the results.","schema":{"type":"integer"}}],"tags":["4337"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedUserOperationWithSafeOperationResponseList"}}},"description":""}}}}}}
```

## GET /tx-service/eth/api/v1/user-operations/{user\_operation\_hash}/

> Returns a UserOperation given its user operation hash

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"security":[{"cookieAuth":[]},{"tokenAuth":[]},{}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sessionid"},"tokenAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Token-based authentication with required prefix \"Token\""}},"schemas":{"UserOperationWithSafeOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"safeOperation":{"oneOf":[{"$ref":"#/components/schemas/SafeOperationResponse"},{"type":"null"}],"readOnly":true}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","safeOperation","sender","signature","userOperationHash","verificationGasLimit"]},"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}},"paths":{"/tx-service/eth/api/v1/user-operations/{user_operation_hash}/":{"get":{"operationId":"user_operations_retrieve","description":"Returns a UserOperation given its user operation hash","parameters":[{"in":"path","name":"user_operation_hash","schema":{"type":"string"},"required":true}],"tags":["4337"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOperationWithSafeOperationResponse"}}},"description":""}}}}}}
```


# Models

## The AllTransactionsSchema object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"AllTransactionsSchema":{"type":"object","description":"Just for the purpose of documenting, don't use it","properties":{"txType1":{"$ref":"#/components/schemas/SafeModuleTransactionWithTransfersResponse"},"txType2":{"$ref":"#/components/schemas/SafeMultisigTransactionWithTransfersResponse"},"txType3":{"$ref":"#/components/schemas/EthereumTxWithTransfersResponse"}},"required":["txType1","txType2","txType3"]},"SafeModuleTransactionWithTransfersResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","transfers","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]},"SafeMultisigTransactionWithTransfersResponse":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"EthereumTxWithTransfersResponse":{"type":"object","properties":{"executionDate":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"data":{"type":"string"},"txHash":{"type":"string"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true},"from":{"type":"string"}},"required":["blockNumber","data","executionDate","from","to","transfers","txHash","txType"]}}}}
```

## The AllTransactionsSchemaSerializerV2 object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"AllTransactionsSchemaSerializerV2":{"type":"object","description":"Just for the purpose of documenting, don't use it","properties":{"txType1":{"$ref":"#/components/schemas/SafeModuleTransactionWithTransfersResponse"},"txType2":{"$ref":"#/components/schemas/SafeMultisigTransactionWithTransfersResponseSerializerV2"},"txType3":{"$ref":"#/components/schemas/EthereumTxWithTransfersResponse"}},"required":["txType1","txType2","txType3"]},"SafeModuleTransactionWithTransfersResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","transfers","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]},"SafeMultisigTransactionWithTransfersResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"EthereumTxWithTransfersResponse":{"type":"object","properties":{"executionDate":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"data":{"type":"string"},"txHash":{"type":"string"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true},"from":{"type":"string"}},"required":["blockNumber","data","executionDate","from","to","transfers","txHash","txType"]}}}}
```

## The CodeErrorResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"CodeErrorResponse":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"arguments":{"type":"array","items":{}}},"required":["arguments","code","message"]}}}}
```

## The Contract object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"Contract":{"type":"object","properties":{"address":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"logoUri":{"type":"string","format":"uri"},"contractAbi":{"$ref":"#/components/schemas/ContractAbi"},"trustedForDelegateCall":{"type":"boolean"}},"required":["address","contractAbi","displayName","logoUri","name","trustedForDelegateCall"]},"ContractAbi":{"type":"object","properties":{"abi":{"type":"array","items":{"type":"object","additionalProperties":{}}},"description":{"type":"string"},"relevance":{"type":"integer"}},"required":["abi","description","relevance"]}}}}
```

## The ContractAbi object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"ContractAbi":{"type":"object","properties":{"abi":{"type":"array","items":{"type":"object","additionalProperties":{}}},"description":{"type":"string"},"relevance":{"type":"integer"}},"required":["abi","description","relevance"]}}}}
```

## The DataDecoder object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"DataDecoder":{"type":"object","properties":{"data":{"type":"string"},"to":{"type":["string","null"]}},"required":["data"]}}}}
```

## The Delegate object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"Delegate":{"type":"object","description":".. deprecated:: 4.38.0\n   Deprecated in favour of DelegateSerializerV2","properties":{"safe":{"type":["string","null"]},"delegate":{"type":"string"},"delegator":{"type":"string"},"signature":{"type":"string"},"label":{"type":"string","maxLength":50}},"required":["delegate","delegator","label","signature"]}}}}
```

## The DelegateSerializerV2 object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"DelegateSerializerV2":{"type":"object","description":"Mixin to validate delegate operations data","properties":{"safe":{"type":["string","null"]},"delegate":{"type":"string"},"delegator":{"type":"string"},"signature":{"type":"string"},"label":{"type":"string","maxLength":50},"expiryDate":{"type":["string","null"],"format":"date-time"}},"required":["delegate","delegator","label","signature"]}}}}
```

## The Erc20Info object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"Erc20Info":{"type":"object","properties":{"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string"}},"required":["decimals","logoUri","name","symbol"]}}}}
```

## The EthereumTxWithTransfersResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"EthereumTxWithTransfersResponse":{"type":"object","properties":{"executionDate":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"data":{"type":"string"},"txHash":{"type":"string"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true},"from":{"type":"string"}},"required":["blockNumber","data","executionDate","from","to","transfers","txHash","txType"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The IndexingStatus object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"IndexingStatus":{"type":"object","properties":{"currentBlockNumber":{"type":"integer"},"currentBlockTimestamp":{"type":"string","format":"date-time"},"erc20BlockNumber":{"type":"integer"},"erc20BlockTimestamp":{"type":"string","format":"date-time"},"erc20Synced":{"type":"boolean"},"masterCopiesBlockNumber":{"type":"integer"},"masterCopiesBlockTimestamp":{"type":"string","format":"date-time"},"masterCopiesSynced":{"type":"boolean"},"synced":{"type":"boolean"}},"required":["currentBlockNumber","currentBlockTimestamp","erc20BlockNumber","erc20BlockTimestamp","erc20Synced","masterCopiesBlockNumber","masterCopiesBlockTimestamp","masterCopiesSynced","synced"]}}}}
```

## The MasterCopyResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"MasterCopyResponse":{"type":"object","properties":{"address":{"type":"string"},"version":{"type":"string"},"deployer":{"type":"string"},"deployedBlockNumber":{"type":"integer"},"lastIndexedBlockNumber":{"type":"integer"},"l2":{"type":"boolean"}},"required":["address","deployedBlockNumber","deployer","l2","lastIndexedBlockNumber","version"]}}}}
```

## The ModulesResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"ModulesResponse":{"type":"object","properties":{"safes":{"type":"array","items":{"type":"string"}}},"required":["safes"]}}}}
```

## The OwnerResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"OwnerResponse":{"type":"object","properties":{"safes":{"type":"array","items":{"type":"string"}}},"required":["safes"]}}}}
```

## The PaginatedAllTransactionsSchemaList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedAllTransactionsSchemaList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/AllTransactionsSchema"}}}},"AllTransactionsSchema":{"type":"object","description":"Just for the purpose of documenting, don't use it","properties":{"txType1":{"$ref":"#/components/schemas/SafeModuleTransactionWithTransfersResponse"},"txType2":{"$ref":"#/components/schemas/SafeMultisigTransactionWithTransfersResponse"},"txType3":{"$ref":"#/components/schemas/EthereumTxWithTransfersResponse"}},"required":["txType1","txType2","txType3"]},"SafeModuleTransactionWithTransfersResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","transfers","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]},"SafeMultisigTransactionWithTransfersResponse":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"EthereumTxWithTransfersResponse":{"type":"object","properties":{"executionDate":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"data":{"type":"string"},"txHash":{"type":"string"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true},"from":{"type":"string"}},"required":["blockNumber","data","executionDate","from","to","transfers","txHash","txType"]}}}}
```

## The PaginatedAllTransactionsSchemaSerializerV2List object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedAllTransactionsSchemaSerializerV2List":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/AllTransactionsSchemaSerializerV2"}}}},"AllTransactionsSchemaSerializerV2":{"type":"object","description":"Just for the purpose of documenting, don't use it","properties":{"txType1":{"$ref":"#/components/schemas/SafeModuleTransactionWithTransfersResponse"},"txType2":{"$ref":"#/components/schemas/SafeMultisigTransactionWithTransfersResponseSerializerV2"},"txType3":{"$ref":"#/components/schemas/EthereumTxWithTransfersResponse"}},"required":["txType1","txType2","txType3"]},"SafeModuleTransactionWithTransfersResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","transfers","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]},"SafeMultisigTransactionWithTransfersResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"EthereumTxWithTransfersResponse":{"type":"object","properties":{"executionDate":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"data":{"type":"string"},"txHash":{"type":"string"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true},"from":{"type":"string"}},"required":["blockNumber","data","executionDate","from","to","transfers","txHash","txType"]}}}}
```

## The PaginatedContractList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedContractList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/Contract"}}}},"Contract":{"type":"object","properties":{"address":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"logoUri":{"type":"string","format":"uri"},"contractAbi":{"$ref":"#/components/schemas/ContractAbi"},"trustedForDelegateCall":{"type":"boolean"}},"required":["address","contractAbi","displayName","logoUri","name","trustedForDelegateCall"]},"ContractAbi":{"type":"object","properties":{"abi":{"type":"array","items":{"type":"object","additionalProperties":{}}},"description":{"type":"string"},"relevance":{"type":"integer"}},"required":["abi","description","relevance"]}}}}
```

## The PaginatedSafeCollectibleResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeCollectibleResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeCollectibleResponse"}}}},"SafeCollectibleResponse":{"type":"object","properties":{"address":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"logoUri":{"type":"string"},"id":{"type":"string"},"uri":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"imageUri":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["address","description","id","imageUri","logoUri","metadata","name","tokenName","tokenSymbol","uri"]}}}}
```

## The PaginatedSafeDelegateResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeDelegateResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeDelegateResponse"}}}},"SafeDelegateResponse":{"type":"object","properties":{"safe":{"type":"string"},"delegate":{"type":"string"},"delegator":{"type":"string"},"label":{"type":"string","maxLength":50},"expiryDate":{"type":"string","format":"date-time"}},"required":["delegate","delegator","expiryDate","label","safe"]}}}}
```

## The PaginatedSafeExportTransactionList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeExportTransactionList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeExportTransaction"}}}},"SafeExportTransaction":{"type":"object","description":"Serializer for the export endpoint that returns transaction data optimized for CSV export","properties":{"safe":{"type":"string"},"From":{"type":"string","title":" from"},"to":{"type":"string"},"amount":{"type":"string"},"assetType":{"type":"string"},"assetAddress":{"type":["string","null"]},"assetSymbol":{"type":["string","null"]},"assetDecimals":{"type":["integer","null"]},"proposerAddress":{"type":["string","null"]},"proposedAt":{"type":["string","null"],"format":"date-time"},"executorAddress":{"type":["string","null"]},"executedAt":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"]},"transactionHash":{"type":"string"},"contractAddress":{"type":["string","null"]},"nonce":{"type":["string","null"]}},"required":["From","amount","assetAddress","assetDecimals","assetSymbol","assetType","contractAddress","executedAt","executorAddress","nonce","note","proposedAt","proposerAddress","safe","to","transactionHash"]}}}}
```

## The PaginatedSafeMessageResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeMessageResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeMessageResponse"}}}},"SafeMessageResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safe":{"type":"string"},"messageHash":{"type":"string"},"message":{},"proposedBy":{"type":"string"},"safeAppId":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeMessage instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":["string","null"],"description":"Prepared signature sorted\n\n:param obj: SafeMessage instance\n:return: Serialized queryset","readOnly":true},"origin":{"type":"string","readOnly":true}},"required":["confirmations","created","message","messageHash","modified","origin","preparedSignature","proposedBy","safe","safeAppId"]}}}}
```

## The PaginatedSafeModuleTransactionResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeModuleTransactionResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeModuleTransactionResponse"}}}},"SafeModuleTransactionResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","value"]}}}}
```

## The PaginatedSafeMultisigConfirmationResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeMultisigConfirmationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeMultisigConfirmationResponse"}}}},"SafeMultisigConfirmationResponse":{"type":"object","properties":{"owner":{"type":"string"},"submissionDate":{"type":"string","format":"date-time"},"transactionHash":{"type":"string","readOnly":true},"signature":{"type":"string"},"signatureType":{"type":"string","readOnly":true}},"required":["owner","signature","signatureType","submissionDate","transactionHash"]}}}}
```

## The PaginatedSafeMultisigTransactionResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeMultisigTransactionResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeMultisigTransactionResponse"}}}},"SafeMultisigTransactionResponse":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","trusted","value"]}}}}
```

## The PaginatedSafeMultisigTransactionResponseSerializerV2List object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeMultisigTransactionResponseSerializerV2List":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeMultisigTransactionResponseSerializerV2"}}}},"SafeMultisigTransactionResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","trusted","value"]}}}}
```

## The PaginatedSafeOperationConfirmationResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeOperationConfirmationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeOperationConfirmationResponse"}}}},"SafeOperationConfirmationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"owner":{"type":"string"},"signature":{"type":"string"},"signatureType":{"type":"string","readOnly":true}},"required":["created","modified","owner","signature","signatureType"]}}}}
```

## The PaginatedSafeOperationWithUserOperationResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedSafeOperationWithUserOperationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SafeOperationWithUserOperationResponse"}}}},"SafeOperationWithUserOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true},"userOperation":{"allOf":[{"$ref":"#/components/schemas/UserOperationResponse"}],"readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","userOperation","validAfter","validUntil"]},"UserOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","sender","signature","userOperationHash","verificationGasLimit"]}}}}
```

## The PaginatedTokenInfoResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedTokenInfoResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/TokenInfoResponse"}}}},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The PaginatedTokenListList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedTokenListList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/TokenList"}}}},"TokenList":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":200},"description":{"type":"string","maxLength":200}},"required":["description","url"]}}}}
```

## The PaginatedTransferWithTokenInfoResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedTransferWithTokenInfoResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}}}},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The PaginatedUserOperationWithSafeOperationResponseList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"PaginatedUserOperationWithSafeOperationResponseList":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer"},"next":{"type":"string","nullable":true,"format":"uri"},"previous":{"type":"string","nullable":true,"format":"uri"},"results":{"type":"array","items":{"$ref":"#/components/schemas/UserOperationWithSafeOperationResponse"}}}},"UserOperationWithSafeOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"safeOperation":{"oneOf":[{"$ref":"#/components/schemas/SafeOperationResponse"},{"type":"null"}],"readOnly":true}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","safeOperation","sender","signature","userOperationHash","verificationGasLimit"]},"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}}}
```

## The SafeBalanceResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeBalanceResponse":{"type":"object","properties":{"tokenAddress":{"type":"string"},"token":{"$ref":"#/components/schemas/Erc20Info"},"balance":{"type":"string"}},"required":["balance","token","tokenAddress"]},"Erc20Info":{"type":"object","properties":{"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string"}},"required":["decimals","logoUri","name","symbol"]}}}}
```

## The SafeCollectibleResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeCollectibleResponse":{"type":"object","properties":{"address":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"logoUri":{"type":"string"},"id":{"type":"string"},"uri":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"imageUri":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["address","description","id","imageUri","logoUri","metadata","name","tokenName","tokenSymbol","uri"]}}}}
```

## The SafeCreationInfoResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeCreationInfoResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"creator":{"type":"string"},"transactionHash":{"type":"string"},"factoryAddress":{"type":"string"},"masterCopy":{"type":["string","null"]},"setupData":{"type":["string","null"]},"saltNonce":{"type":["string","null"]},"dataDecoded":{"type":"object","additionalProperties":{},"readOnly":true},"userOperation":{"oneOf":[{"$ref":"#/components/schemas/UserOperationWithSafeOperationResponse"},{"type":"null"}]}},"required":["created","creator","dataDecoded","factoryAddress","masterCopy","saltNonce","setupData","transactionHash","userOperation"]},"UserOperationWithSafeOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"safeOperation":{"oneOf":[{"$ref":"#/components/schemas/SafeOperationResponse"},{"type":"null"}],"readOnly":true}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","safeOperation","sender","signature","userOperationHash","verificationGasLimit"]},"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}}}
```

## The SafeDelegateResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeDelegateResponse":{"type":"object","properties":{"safe":{"type":"string"},"delegate":{"type":"string"},"delegator":{"type":"string"},"label":{"type":"string","maxLength":50},"expiryDate":{"type":"string","format":"date-time"}},"required":["delegate","delegator","expiryDate","label","safe"]}}}}
```

## The SafeDeployment object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeDeployment":{"type":"object","properties":{"version":{"type":"string","maxLength":10},"contracts":{"type":"array","items":{"$ref":"#/components/schemas/SafeDeploymentContract"}}},"required":["contracts","version"]},"SafeDeploymentContract":{"type":"object","properties":{"contractName":{"type":"string"},"address":{"type":["string","null"]}},"required":["address","contractName"]}}}}
```

## The SafeDeploymentContract object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeDeploymentContract":{"type":"object","properties":{"contractName":{"type":"string"},"address":{"type":["string","null"]}},"required":["address","contractName"]}}}}
```

## The SafeExportTransaction object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeExportTransaction":{"type":"object","description":"Serializer for the export endpoint that returns transaction data optimized for CSV export","properties":{"safe":{"type":"string"},"From":{"type":"string","title":" from"},"to":{"type":"string"},"amount":{"type":"string"},"assetType":{"type":"string"},"assetAddress":{"type":["string","null"]},"assetSymbol":{"type":["string","null"]},"assetDecimals":{"type":["integer","null"]},"proposerAddress":{"type":["string","null"]},"proposedAt":{"type":["string","null"],"format":"date-time"},"executorAddress":{"type":["string","null"]},"executedAt":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"]},"transactionHash":{"type":"string"},"contractAddress":{"type":["string","null"]},"nonce":{"type":["string","null"]}},"required":["From","amount","assetAddress","assetDecimals","assetSymbol","assetType","contractAddress","executedAt","executorAddress","nonce","note","proposedAt","proposerAddress","safe","to","transactionHash"]}}}}
```

## The SafeInfoResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeInfoResponse":{"type":"object","properties":{"address":{"type":"string"},"nonce":{"type":"string"},"threshold":{"type":"integer"},"owners":{"type":"array","items":{"type":"string"}},"masterCopy":{"type":"string"},"modules":{"type":"array","items":{"type":"string"}},"fallbackHandler":{"type":"string"},"guard":{"type":"string"},"version":{"type":["string","null"]}},"required":["address","fallbackHandler","guard","masterCopy","modules","nonce","owners","threshold","version"]}}}}
```

## The SafeMessage object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMessage":{"type":"object","properties":{"message":{},"safeAppId":{"type":["integer","null"],"minimum":0},"signature":{"type":"string"},"origin":{"type":["string","null"],"maxLength":200}},"required":["message","signature"]}}}}
```

## The SafeMessageResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMessageResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safe":{"type":"string"},"messageHash":{"type":"string"},"message":{},"proposedBy":{"type":"string"},"safeAppId":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeMessage instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":["string","null"],"description":"Prepared signature sorted\n\n:param obj: SafeMessage instance\n:return: Serialized queryset","readOnly":true},"origin":{"type":"string","readOnly":true}},"required":["confirmations","created","message","messageHash","modified","origin","preparedSignature","proposedBy","safe","safeAppId"]}}}}
```

## The SafeMessageSignature object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMessageSignature":{"type":"object","properties":{"signature":{"type":"string"}},"required":["signature"]}}}}
```

## The SafeModuleTransactionResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeModuleTransactionResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","value"]}}}}
```

## The SafeModuleTransactionWithTransfersResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeModuleTransactionWithTransfersResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"isSuccessful":{"type":"boolean","readOnly":true},"transactionHash":{"type":"string"},"safe":{"type":"string"},"module":{"type":"string"},"to":{"type":"string"},"value":{"type":"string","format":"decimal","pattern":"^-?\\d{0,78}(?:\\.\\d{0,0})?$"},"data":{"type":["string","null"]},"operation":{"enum":[0,1,2],"type":"integer","description":"* `0` - CALL\n* `1` - DELEGATE_CALL\n* `2` - CREATE","minimum":0,"maximum":32767},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"moduleTransactionId":{"type":"string","description":"Internally calculated parameter to uniquely identify a moduleTransaction \n`ModuleTransactionId = i+tx_hash+trace_address`"},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["blockNumber","created","data","dataDecoded","executionDate","isSuccessful","module","moduleTransactionId","operation","safe","to","transactionHash","transfers","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The SafeMultisigConfirmation object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigConfirmation":{"type":"object","properties":{"signature":{"type":"string"}},"required":["signature"]}}}}
```

## The SafeMultisigConfirmationResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigConfirmationResponse":{"type":"object","properties":{"owner":{"type":"string"},"submissionDate":{"type":"string","format":"date-time"},"transactionHash":{"type":"string","readOnly":true},"signature":{"type":"string"},"signatureType":{"type":"string","readOnly":true}},"required":["owner","signature","signatureType","submissionDate","transactionHash"]}}}}
```

## The SafeMultisigTransaction object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransaction":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"integer","minimum":0},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"integer","minimum":0},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"contractTransactionHash":{"type":"string"},"sender":{"type":"string"},"signature":{"type":["string","null"]},"origin":{"type":["string","null"],"maxLength":200}},"required":["baseGas","contractTransactionHash","gasPrice","nonce","operation","safe","safeTxGas","sender","to","value"]}}}}
```

## The SafeMultisigTransactionEstimate object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransactionEstimate":{"type":"object","properties":{"to":{"type":"string"},"value":{"type":"integer","minimum":0},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0}},"required":["operation","to","value"]}}}}
```

## The SafeMultisigTransactionEstimateResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransactionEstimateResponse":{"type":"object","properties":{"safeTxGas":{"type":"string"}},"required":["safeTxGas"]}}}}
```

## The SafeMultisigTransactionResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransactionResponse":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","trusted","value"]}}}}
```

## The SafeMultisigTransactionResponseSerializerV2 object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransactionResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","trusted","value"]}}}}
```

## The SafeMultisigTransactionWithTransfersResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransactionWithTransfersResponse":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"integer","minimum":0},"baseGas":{"type":"integer","minimum":0},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"integer","minimum":0},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The SafeMultisigTransactionWithTransfersResponseSerializerV2 object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeMultisigTransactionWithTransfersResponseSerializerV2":{"type":"object","properties":{"safe":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"data":{"type":["string","null"]},"operation":{"type":"integer","minimum":0},"gasToken":{"type":["string","null"]},"safeTxGas":{"type":"string"},"baseGas":{"type":"string"},"gasPrice":{"type":"string"},"refundReceiver":{"type":["string","null"]},"nonce":{"type":"string"},"executionDate":{"type":"string","format":"date-time"},"submissionDate":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"blockNumber":{"type":["integer","null"],"readOnly":true},"transactionHash":{"type":"string"},"safeTxHash":{"type":"string"},"proposer":{"type":"string"},"proposedByDelegate":{"type":["string","null"]},"executor":{"type":["string","null"],"readOnly":true},"isExecuted":{"type":"boolean"},"isSuccessful":{"type":["boolean","null"],"readOnly":true},"ethGasPrice":{"type":["string","null"],"readOnly":true},"maxFeePerGas":{"type":["string","null"],"readOnly":true},"maxPriorityFeePerGas":{"type":["string","null"],"readOnly":true},"gasUsed":{"type":["integer","null"],"readOnly":true},"fee":{"type":["integer","null"],"readOnly":true},"origin":{"type":"string","readOnly":true},"dataDecoded":{"type":"string","deprecated":true,"description":"This field is deprecated and will be removed in future versions. Refer to decoder service [documentation](https://docs.safe.global/core-api/safe-decoder-service-reference#Data-decoder) for decoding guidance.","readOnly":true},"confirmationsRequired":{"type":"integer"},"confirmations":{"type":"object","additionalProperties":{},"description":"Validate and check integrity of confirmations queryset\n\n:param obj: MultisigConfirmation instance\n:return: Serialized queryset\n:raises InternalValidationError: If any inconsistency is detected","readOnly":true},"trusted":{"type":"boolean"},"signatures":{"type":["string","null"],"readOnly":true},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferWithTokenInfoResponse"}},"txType":{"type":"string","readOnly":true}},"required":["baseGas","blockNumber","confirmations","confirmationsRequired","dataDecoded","ethGasPrice","executionDate","executor","fee","gasPrice","gasUsed","isExecuted","isSuccessful","maxFeePerGas","maxPriorityFeePerGas","modified","nonce","operation","origin","proposedByDelegate","proposer","safe","safeTxGas","safeTxHash","signatures","submissionDate","to","transactionHash","transfers","trusted","txType","value"]},"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The SafeOperation object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeOperation":{"type":"object","description":"Mixin class to validate SafeOperation signatures. `_get_owners` can be overridden to define\nthe valid owners to sign","properties":{"nonce":{"type":"integer","minimum":0},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"integer","minimum":0},"verificationGasLimit":{"type":"integer","minimum":0},"preVerificationGas":{"type":"integer","minimum":0},"maxFeePerGas":{"type":"integer","minimum":0},"maxPriorityFeePerGas":{"type":"integer","minimum":0},"paymasterAndData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"validAfter":{"type":["string","null"],"format":"date-time"},"validUntil":{"type":["string","null"],"format":"date-time"},"moduleAddress":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","initCode","maxFeePerGas","maxPriorityFeePerGas","moduleAddress","nonce","paymasterAndData","preVerificationGas","signature","validAfter","validUntil","verificationGasLimit"]}}}}
```

## The SafeOperationConfirmation object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeOperationConfirmation":{"type":"object","description":"Validate new confirmations for an existing `SafeOperation`","properties":{"signature":{"type":"string"}},"required":["signature"]}}}}
```

## The SafeOperationConfirmationResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeOperationConfirmationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"owner":{"type":"string"},"signature":{"type":"string"},"signatureType":{"type":"string","readOnly":true}},"required":["created","modified","owner","signature","signatureType"]}}}}
```

## The SafeOperationResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}}}
```

## The SafeOperationWithUserOperationResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"SafeOperationWithUserOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true},"userOperation":{"allOf":[{"$ref":"#/components/schemas/UserOperationResponse"}],"readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","userOperation","validAfter","validUntil"]},"UserOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","sender","signature","userOperationHash","verificationGasLimit"]}}}}
```

## The TokenInfoResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The TokenList object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"TokenList":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":200},"description":{"type":"string","maxLength":200}},"required":["description","url"]}}}}
```

## The TransferWithTokenInfoResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"TransferWithTokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","description":"Sometimes ERC20/721 `Transfer` events look the same, if token info is available better use that information\nto check\n\n:param obj:\n:return: `TransferType` as a string","readOnly":true},"executionDate":{"type":"string","format":"date-time"},"blockNumber":{"type":"integer"},"transactionHash":{"type":"string"},"to":{"type":"string"},"value":{"type":["string","null"]},"tokenId":{"type":["string","null"]},"tokenAddress":{"type":["string","null"]},"transferId":{"type":"string","readOnly":true,"description":"Internally calculated parameter to uniquely identify a transfer \nToken transfers are calculated as `transferId = e+tx_hash+log_index` \nEther transfers are calculated as `transferId = i+tx_hash+trace_address`"},"tokenInfo":{"$ref":"#/components/schemas/TokenInfoResponse"},"from":{"type":"string"}},"required":["blockNumber","executionDate","from","to","tokenId","tokenInfo","transactionHash","transferId","type","value"]},"TokenInfoResponse":{"type":"object","properties":{"type":{"type":"string","readOnly":true},"address":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"logoUri":{"type":"string","readOnly":true},"trusted":{"type":"boolean"}},"required":["address","decimals","logoUri","name","symbol","trusted","type"]}}}}
```

## The UserOperationResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"UserOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","sender","signature","userOperationHash","verificationGasLimit"]}}}}
```

## The UserOperationWithSafeOperationResponse object

```json
{"openapi":"3.1.0","info":{"title":"Safe Transaction Service","version":"5.33.1"},"components":{"schemas":{"UserOperationWithSafeOperationResponse":{"type":"object","properties":{"ethereumTxHash":{"type":"string"},"sender":{"type":"string"},"userOperationHash":{"type":"string"},"nonce":{"type":"string"},"initCode":{"type":["string","null"]},"callData":{"type":["string","null"]},"callGasLimit":{"type":"string"},"verificationGasLimit":{"type":"string"},"preVerificationGas":{"type":"string"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"paymaster":{"type":["string","null"]},"paymasterData":{"type":["string","null"]},"signature":{"type":"string"},"entryPoint":{"type":"string"},"safeOperation":{"oneOf":[{"$ref":"#/components/schemas/SafeOperationResponse"},{"type":"null"}],"readOnly":true}},"required":["callData","callGasLimit","entryPoint","ethereumTxHash","initCode","maxFeePerGas","maxPriorityFeePerGas","nonce","paymaster","paymasterData","preVerificationGas","safeOperation","sender","signature","userOperationHash","verificationGasLimit"]},"SafeOperationResponse":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"safeOperationHash":{"type":"string"},"validAfter":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"moduleAddress":{"type":"string"},"confirmations":{"type":"object","additionalProperties":{},"description":"Filters confirmations queryset\n\n:param obj: SafeOperation instance\n:return: Serialized queryset","readOnly":true},"preparedSignature":{"type":"string","readOnly":true}},"required":["confirmations","created","modified","moduleAddress","preparedSignature","safeOperationHash","validAfter","validUntil"]}}}}
```


