> ## Documentation Index
> Fetch the complete documentation index at: https://developers.fireblocks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Typed messages

> Create a TYPED_MESSAGE transaction to sign an off-chain EIP-191, EIP-712, TIP-191 or Bitcoin message with a vault account key.

The examples on this page assume you have an initialised client, as described in [Choose an interface](/transactions/create-transactions#choose-an-interface). Typed message signing is performed through the [Create transaction](/reference/createtransaction) endpoint by setting `operation` to `TYPED_MESSAGE`, and the result is a signature over a readable message rather than an on-chain transfer. For the concepts behind typed messages and how to enable the feature in your workspace, see [Typed Message Signing](/docs/typed-message-signing-1).

## Request structure

The following fields are required for any typed message signing request.

| Field | Description |
| - | - |
| `operation` | The transaction operation type, which is `TYPED_MESSAGE` for typed message signing. |
| `assetId` | The network to sign for, which is `ETH` for Ethereum and other EVM networks, `TRX` for Tron or `BTC` for Bitcoin. On a testnet you can use the matching testnet asset ID, such as `ETH_TEST5`. |
| `source` | The vault account whose key signs the message. |
| `extraParameters` | An object that contains `rawMessageData`, which holds a `messages` array. Each entry in the array sets the `content` to sign and the message `type`. |

The `type` of each message is one of `EIP191`, `EIP712`, `TIP191` or `BTC_MESSAGE`.

| `type` | Used for |
| - | - |
| `EIP191` | Ethereum and EVM personal messages. |
| `EIP712` | Ethereum and EVM typed structured data. |
| `TIP191` | Tron personal messages. |
| `BTC_MESSAGE` | Bitcoin personal messages. |

<Note>
  You do not need to add a prefix to the message before you submit it to the API. The only requirement is that for any message type other than `EIP712` the `content` must be submitted in hexadecimal format, whereas for `EIP712` the `content` is the typed data object itself.
</Note>

For the full list of `rawMessageData` fields see [Raw signing objects](/reference/raw-signing-objects#rawmessagedata), and for all request and response schemas see the [Create transaction](/reference/createtransaction) API reference. To avoid creating duplicate requests when you retry, set a unique `externalTxId` on every request, as described in [Transaction idempotency](/reference/api-idempotency#transaction-idempotency).

## Sign an EIP-191 personal message

This example signs the message `Hello, Ethereum!` from a vault account on the Sepolia testnet, where the message is submitted as a hex string.

<Tabs>
  <Tab title="SDK">
    <CodeGroup>
      ```typescript TypeScript theme={"system"}
      import { TransactionOperation, TransferPeerPathType } from "@fireblocks/ts-sdk";

      const response = await fireblocks.transactions.createTransaction({
        transactionRequest: {
          operation: TransactionOperation.TypedMessage,
          assetId: "ETH_TEST5",
          source: {
            type: TransferPeerPathType.VaultAccount,
            id: "<SOURCE_VAULT_ACCOUNT_ID>",
          },
          note: "EIP-191 message", // Optional - Free-text note shown in your workspace, not sent to the blockchain
          extraParameters: {
            rawMessageData: {
              messages: [
                {
                  content: Buffer.from("Hello, Ethereum!").toString("hex"), // Hex-encoded message
                  type: "EIP191",
                },
              ],
            },
          },
          externalTxId: "<YOUR_UNIQUE_ID>", // Optional - Your unique ID; Fireblocks rejects later requests with the same value
        },
      });
      console.log(response.data);
      ```

      ```python Python theme={"system"}
      from fireblocks.models.transaction_request import TransactionRequest
      from fireblocks.models.transaction_operation import TransactionOperation
      from fireblocks.models.source_transfer_peer_path import SourceTransferPeerPath
      from fireblocks.models.transfer_peer_path_type import TransferPeerPathType
      from fireblocks.models.extra_parameters import ExtraParameters

      transaction_request = TransactionRequest(
          operation=TransactionOperation.TYPED_MESSAGE,
          asset_id="ETH_TEST5",
          source=SourceTransferPeerPath(
              type=TransferPeerPathType.VAULT_ACCOUNT,
              id="<SOURCE_VAULT_ACCOUNT_ID>",
          ),
          note="EIP-191 message",  # Optional - Free-text note shown in your workspace, not sent to the blockchain
          extra_parameters=ExtraParameters(
              raw_message_data={
                  "messages": [
                      {
                          "content": "Hello, Ethereum!".encode().hex(),  # Hex-encoded message
                          "type": "EIP191",
                      }
                  ]
              }
          ),
          external_tx_id="<YOUR_UNIQUE_ID>",  # Optional - Your unique ID; Fireblocks rejects later requests with the same value
      )

      future = fireblocks.transactions.create_transaction(
          transaction_request=transaction_request,
      )
      print(future.result().data)
      ```

      ```java Java theme={"system"}
      import com.fireblocks.sdk.model.*;
      import java.util.List;
      import java.util.Map;

      TransactionRequest transactionRequest = new TransactionRequest()
          .operation(TransactionOperation.TYPED_MESSAGE)
          .assetId("ETH_TEST5")
          .source(new SourceTransferPeerPath()
              .type(TransferPeerPathType.VAULT_ACCOUNT)
              .id("<SOURCE_VAULT_ACCOUNT_ID>"))
          .note("EIP-191 message") // Optional - Free-text note shown in your workspace, not sent to the blockchain
          .extraParameters(new ExtraParameters().rawMessageData(Map.of(
              "messages", List.of(Map.of(
                  "content", "48656c6c6f2c20457468657265756d21", // Hex-encoded "Hello, Ethereum!"
                  "type", "EIP191")))))
          .externalTxId("<YOUR_UNIQUE_ID>"); // Optional - Your unique ID; Fireblocks rejects later requests with the same value

      CreateTransactionResponse transaction = fireblocks.transactions()
          .createTransaction(
              transactionRequest,
              null, // Optional - xEndUserWalletId (UUID), only for end-user wallet operations
              null) // Optional - Not used for transactions; use externalTxId in the request body
          .get()
          .getData();
      System.out.println(transaction);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="API">
    ```http theme={"system"}
    POST /v1/transactions
    ```

    ```json theme={"system"}
    {
      "operation": "TYPED_MESSAGE",
      "assetId": "ETH_TEST5",
      "source": { "type": "VAULT_ACCOUNT", "id": "<SOURCE_VAULT_ACCOUNT_ID>" },
      "note": "EIP-191 message",
      "extraParameters": {
        "rawMessageData": {
          "messages": [
            { "content": "48656c6c6f2c20457468657265756d21", "type": "EIP191" }
          ]
        }
      },
      "externalTxId": "<YOUR_UNIQUE_ID>"
    }
    ```

    Sign the request with a JWT; see [Signing a request](/reference/signing-a-request-jwt-structure).
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    # Preview the request without sending it
    fireblocks transactions create-transaction \
      --data '{"operation":"TYPED_MESSAGE","assetId":"ETH_TEST5","source":{"type":"VAULT_ACCOUNT","id":"<SOURCE_VAULT_ACCOUNT_ID>"},"extraParameters":{"rawMessageData":{"messages":[{"content":"48656c6c6f2c20457468657265756d21","type":"EIP191"}]}},"externalTxId":"<YOUR_UNIQUE_ID>"}' \
      --dry-run

    # Execute
    fireblocks transactions create-transaction \
      --data '{"operation":"TYPED_MESSAGE","assetId":"ETH_TEST5","source":{"type":"VAULT_ACCOUNT","id":"<SOURCE_VAULT_ACCOUNT_ID>"},"extraParameters":{"rawMessageData":{"messages":[{"content":"48656c6c6f2c20457468657265756d21","type":"EIP191"}]}},"externalTxId":"<YOUR_UNIQUE_ID>"}' \
      --no-confirm
    ```
  </Tab>
</Tabs>

## Sign an EIP-712 typed data message

For `EIP712` the `content` is the typed data object, made up of `types`, `primaryType`, `domain` and `message`, rather than a hex string. The following example signs a `Permit` message, using placeholder addresses that you replace with your own values.

<Tabs>
  <Tab title="SDK">
    <CodeGroup>
      ```typescript TypeScript theme={"system"}
      import { TransactionOperation, TransferPeerPathType } from "@fireblocks/ts-sdk";

      const eip712Message = {
        types: {
          EIP712Domain: [
            { name: "name", type: "string" },
            { name: "version", type: "string" },
            { name: "chainId", type: "uint256" },
            { name: "verifyingContract", type: "address" },
          ],
          Permit: [
            { name: "holder", type: "address" },
            { name: "spender", type: "address" },
            { name: "nonce", type: "uint256" },
            { name: "expiry", type: "uint256" },
            { name: "allowed", type: "bool" },
          ],
        },
        primaryType: "Permit",
        domain: {
          name: "<TOKEN_NAME>",
          version: "1",
          chainId: 11155111, // Sepolia; use the chain ID of the EVM network you are signing for
          verifyingContract: "<CONTRACT_ADDRESS>",
        },
        message: {
          holder: "<HOLDER_ADDRESS>",
          spender: "<SPENDER_ADDRESS>",
          nonce: 123,
          expiry: 1655467980,
          allowed: true,
        },
      };

      const response = await fireblocks.transactions.createTransaction({
        transactionRequest: {
          operation: TransactionOperation.TypedMessage,
          assetId: "ETH_TEST5",
          source: {
            type: TransferPeerPathType.VaultAccount,
            id: "<SOURCE_VAULT_ACCOUNT_ID>",
          },
          note: "EIP-712 message", // Optional - Free-text note shown in your workspace, not sent to the blockchain
          extraParameters: {
            rawMessageData: {
              messages: [{ content: eip712Message, type: "EIP712" }],
            },
          },
          externalTxId: "<YOUR_UNIQUE_ID>", // Optional - Your unique ID; Fireblocks rejects later requests with the same value
        },
      });
      console.log(response.data);
      ```

      ```python Python theme={"system"}
      from fireblocks.models.transaction_request import TransactionRequest
      from fireblocks.models.transaction_operation import TransactionOperation
      from fireblocks.models.source_transfer_peer_path import SourceTransferPeerPath
      from fireblocks.models.transfer_peer_path_type import TransferPeerPathType
      from fireblocks.models.extra_parameters import ExtraParameters

      eip712_message = {
          "types": {
              "EIP712Domain": [
                  {"name": "name", "type": "string"},
                  {"name": "version", "type": "string"},
                  {"name": "chainId", "type": "uint256"},
                  {"name": "verifyingContract", "type": "address"},
              ],
              "Permit": [
                  {"name": "holder", "type": "address"},
                  {"name": "spender", "type": "address"},
                  {"name": "nonce", "type": "uint256"},
                  {"name": "expiry", "type": "uint256"},
                  {"name": "allowed", "type": "bool"},
              ],
          },
          "primaryType": "Permit",
          "domain": {
              "name": "<TOKEN_NAME>",
              "version": "1",
              "chainId": 11155111,  # Sepolia; use the chain ID of the EVM network you are signing for
              "verifyingContract": "<CONTRACT_ADDRESS>",
          },
          "message": {
              "holder": "<HOLDER_ADDRESS>",
              "spender": "<SPENDER_ADDRESS>",
              "nonce": 123,
              "expiry": 1655467980,
              "allowed": True,
          },
      }

      transaction_request = TransactionRequest(
          operation=TransactionOperation.TYPED_MESSAGE,
          asset_id="ETH_TEST5",
          source=SourceTransferPeerPath(
              type=TransferPeerPathType.VAULT_ACCOUNT,
              id="<SOURCE_VAULT_ACCOUNT_ID>",
          ),
          note="EIP-712 message",  # Optional - Free-text note shown in your workspace, not sent to the blockchain
          extra_parameters=ExtraParameters(
              raw_message_data={
                  "messages": [{"content": eip712_message, "type": "EIP712"}]
              }
          ),
          external_tx_id="<YOUR_UNIQUE_ID>",  # Optional - Your unique ID; Fireblocks rejects later requests with the same value
      )

      future = fireblocks.transactions.create_transaction(
          transaction_request=transaction_request,
      )
      print(future.result().data)
      ```

      ```java Java theme={"system"}
      import com.fireblocks.sdk.model.*;
      import java.util.List;
      import java.util.Map;

      Map<String, Object> eip712Message = Map.of(
          "types", Map.of(
              "EIP712Domain", List.of(
                  Map.of("name", "name", "type", "string"),
                  Map.of("name", "version", "type", "string"),
                  Map.of("name", "chainId", "type", "uint256"),
                  Map.of("name", "verifyingContract", "type", "address")),
              "Permit", List.of(
                  Map.of("name", "holder", "type", "address"),
                  Map.of("name", "spender", "type", "address"),
                  Map.of("name", "nonce", "type", "uint256"),
                  Map.of("name", "expiry", "type", "uint256"),
                  Map.of("name", "allowed", "type", "bool"))),
          "primaryType", "Permit",
          "domain", Map.of(
              "name", "<TOKEN_NAME>",
              "version", "1",
              "chainId", 11155111, // Sepolia; use the chain ID of the EVM network you are signing for
              "verifyingContract", "<CONTRACT_ADDRESS>"),
          "message", Map.of(
              "holder", "<HOLDER_ADDRESS>",
              "spender", "<SPENDER_ADDRESS>",
              "nonce", 123,
              "expiry", 1655467980,
              "allowed", true));

      TransactionRequest transactionRequest = new TransactionRequest()
          .operation(TransactionOperation.TYPED_MESSAGE)
          .assetId("ETH_TEST5")
          .source(new SourceTransferPeerPath()
              .type(TransferPeerPathType.VAULT_ACCOUNT)
              .id("<SOURCE_VAULT_ACCOUNT_ID>"))
          .note("EIP-712 message") // Optional - Free-text note shown in your workspace, not sent to the blockchain
          .extraParameters(new ExtraParameters().rawMessageData(Map.of(
              "messages", List.of(Map.of("content", eip712Message, "type", "EIP712")))))
          .externalTxId("<YOUR_UNIQUE_ID>"); // Optional - Your unique ID; Fireblocks rejects later requests with the same value

      CreateTransactionResponse transaction = fireblocks.transactions()
          .createTransaction(
              transactionRequest,
              null, // Optional - xEndUserWalletId (UUID), only for end-user wallet operations
              null) // Optional - Not used for transactions; use externalTxId in the request body
          .get()
          .getData();
      System.out.println(transaction);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="API">
    ```http theme={"system"}
    POST /v1/transactions
    ```

    ```json theme={"system"}
    {
      "operation": "TYPED_MESSAGE",
      "assetId": "ETH_TEST5",
      "source": { "type": "VAULT_ACCOUNT", "id": "<SOURCE_VAULT_ACCOUNT_ID>" },
      "note": "EIP-712 message",
      "extraParameters": {
        "rawMessageData": {
          "messages": [
            {
              "type": "EIP712",
              "content": {
                "types": {
                  "EIP712Domain": [
                    { "name": "name", "type": "string" },
                    { "name": "version", "type": "string" },
                    { "name": "chainId", "type": "uint256" },
                    { "name": "verifyingContract", "type": "address" }
                  ],
                  "Permit": [
                    { "name": "holder", "type": "address" },
                    { "name": "spender", "type": "address" },
                    { "name": "nonce", "type": "uint256" },
                    { "name": "expiry", "type": "uint256" },
                    { "name": "allowed", "type": "bool" }
                  ]
                },
                "primaryType": "Permit",
                "domain": {
                  "name": "<TOKEN_NAME>",
                  "version": "1",
                  "chainId": 11155111,
                  "verifyingContract": "<CONTRACT_ADDRESS>"
                },
                "message": {
                  "holder": "<HOLDER_ADDRESS>",
                  "spender": "<SPENDER_ADDRESS>",
                  "nonce": 123,
                  "expiry": 1655467980,
                  "allowed": true
                }
              }
            }
          ]
        }
      },
      "externalTxId": "<YOUR_UNIQUE_ID>"
    }
    ```

    Sign the request with a JWT; see [Signing a request](/reference/signing-a-request-jwt-structure).
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    # Preview the request without sending it
    fireblocks transactions create-transaction \
      --data '{"operation":"TYPED_MESSAGE","assetId":"ETH_TEST5","source":{"type":"VAULT_ACCOUNT","id":"<SOURCE_VAULT_ACCOUNT_ID>"},"extraParameters":{"rawMessageData":{"messages":[{"type":"EIP712","content":{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Permit":[{"name":"holder","type":"address"},{"name":"spender","type":"address"},{"name":"nonce","type":"uint256"},{"name":"expiry","type":"uint256"},{"name":"allowed","type":"bool"}]},"primaryType":"Permit","domain":{"name":"<TOKEN_NAME>","version":"1","chainId":11155111,"verifyingContract":"<CONTRACT_ADDRESS>"},"message":{"holder":"<HOLDER_ADDRESS>","spender":"<SPENDER_ADDRESS>","nonce":123,"expiry":1655467980,"allowed":true}}}]}},"externalTxId":"<YOUR_UNIQUE_ID>"}' \
      --dry-run

    # Execute by re-running the same command with --no-confirm instead of --dry-run
    ```
  </Tab>
</Tabs>

## Network-specific guides

Each network has its own message formats and signature structure, which the following pages cover in detail.

* [Sign typed messages for Ethereum and EVM networks](/reference/sign-typed-messages-for-ethereum-and-evm-networks) covers `EIP191` and `EIP712`. For any EVM-compatible network other than Ethereum the `assetId` you pass is still `ETH`.
* [Sign typed messages in Tron](/reference/signing-typed-messages-in-tron) covers `TIP191`.
* [Sign typed messages in Bitcoin](/reference/signing-typed-messages-in-bitcoin) covers `BTC_MESSAGE`.

After the request is created you can retrieve the signature from the transaction once it completes, as described in [Monitoring transaction status](/reference/monitoring-transaction-status).
