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

# Signing Operations

> Sign transactions and messages for EVM, Solana, and Bitcoin networks using Core API v2

All signing operations in v2 require an `op_jwt` — a short-lived JWT (max 5 minutes) for the user — along with the `access_key` returned at wallet creation. The Nitro Enclave verifies the JWT and matches the caller's identity against the wallet's binding before authorizing the operation.

## Transaction Signing

Sign transaction payloads for different blockchain networks. The payload format varies by network type.

### EVM Transaction Signing

Sign Ethereum and EVM-compatible chain transactions.

```bash cURL icon="square-terminal" theme={null}
curl -X POST 'https://tee.magiclabs.com/v2/api/wallet/sign_transaction' \
  -H 'Content-Type: application/json' \
  -H 'x-magic-secret-key: sk_live_XXXXXXXX' \
  -d '{
    "op_jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGgueW91cmFwcC5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vdGVlLm1hZ2ljbGFicy5jb20iLCJpYXQiOjE3NTE2NzIwMDAsImV4cCI6MTc1MTY3MjMwMH0.SIGNATURE",
    "wallet_id": "e982b4a3-14d3-4d66-a3ac-fadfc3xxxxxx",
    "access_key": "key_shard",
    "payload": {
      "type": 2,
      "chainId": 888888,
      "nonce": 1,
      "value": "0x616263646566",
      "gas": 100000,
      "maxFeePerGas": 2000000000,
      "maxPriorityFeePerGas": 2000000000,
      "to": "0x0aA818d7952c0B6a56ee97581708866C154f63AD"
    }
  }'
```

### Solana Transaction Signing

Sign Solana blockchain transactions.

```bash cURL icon="square-terminal" theme={null}
curl -X POST 'https://tee.magiclabs.com/v2/api/wallet/sign_transaction' \
  -H 'Content-Type: application/json' \
  -H 'x-magic-secret-key: sk_live_XXXXXXXX' \
  -d '{
    "op_jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGgueW91cmFwcC5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vdGVlLm1hZ2ljbGFicy5jb20iLCJpYXQiOjE3NTE2NzIwMDAsImV4cCI6MTc1MTY3MjMwMH0.SIGNATURE",
    "wallet_id": "e982b4a3-14d3-4d66-a3ac-fadfc3xxxxxx",
    "access_key": "key_shard",
    "payload": "transaction_base64"
  }'
```

### Bitcoin Transaction Signing

Sign Bitcoin blockchain transactions.

```bash cURL icon="square-terminal" theme={null}
curl -X POST 'https://tee.magiclabs.com/v2/api/wallet/sign_transaction' \
  -H 'Content-Type: application/json' \
  -H 'x-magic-secret-key: sk_live_XXXXXXXX' \
  -d '{
    "op_jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGgueW91cmFwcC5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vdGVlLm1hZ2ljbGFicy5jb20iLCJpYXQiOjE3NTE2NzIwMDAsImV4cCI6MTc1MTY3MjMwMH0.SIGNATURE",
    "wallet_id": "e982b4a3-14d3-4d66-a3ac-fadfc3xxxxxx",
    "access_key": "key_shard",
    "payload": {
      "inputs": [
        {
          "address": "bc1q59ehj6mza09gk8v5558dqpy8m4c...",
          "value": 5,
          "txid": "d12beb629cebe086149c9c7b558f09531f8...",
          "tx_num": 0
        }
      ],
      "outputs": [
        {
          "address": "bc1q59ehj6mza09gk8v5558dqpy8m4c...",
          "value": 5
        }
      ]
    }
  }'
```

**Transaction Signing Response:**

```json theme={null}
{
  "data": {
    "signed_transaction": "0xabc...",
    "transaction_hash": "0xabc..."
  }
}
```

### Request Parameters

<ParamField body="op_jwt" type="string" required post={["Body"]}>
  Short-lived JWT (max 5 minutes) for the user performing the operation. Verified inside the enclave against the wallet's identity binding.
</ParamField>

<ParamField body="wallet_id" type="string" required post={["Body"]}>
  The UUID of the wallet to sign with.
</ParamField>

<ParamField body="access_key" type="string" required post={["Body"]}>
  The access key returned from wallet creation. Retrieve from your database.
</ParamField>

<ParamField body="payload" type="object" required post={["Body"]}>
  Transaction payload to be signed. Format varies by network:

  * **EVM**: Standard Ethereum transaction object
  * **Solana**: Base64-encoded transaction string
  * **Bitcoin**: Object with inputs and outputs arrays
</ParamField>

### Response Fields

<ResponseField name="data.signed_transaction" type="string">
  The signed transaction ready for broadcast to the blockchain.
</ResponseField>

<ResponseField name="data.transaction_hash" type="string">
  The transaction hash that will be generated when the transaction is submitted to the network.
</ResponseField>

## Message Signing

### Sign Data

Sign arbitrary data using keccak256 hash (EVM only).

```bash cURL icon="square-terminal" theme={null}
curl -X POST 'https://tee.magiclabs.com/v2/api/wallet/sign_data' \
  -H 'Content-Type: application/json' \
  -H 'x-magic-secret-key: sk_live_XXXXXXXX' \
  -d '{
    "op_jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGgueW91cmFwcC5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vdGVlLm1hZ2ljbGFicy5jb20iLCJpYXQiOjE3NTE2NzIwMDAsImV4cCI6MTc1MTY3MjMwMH0.SIGNATURE",
    "wallet_id": "e982b4a3-14d3-4d66-a3ac-fadfc3xxxxxx",
    "access_key": "key_shard",
    "raw_data_hash": "keccak256_string_hash"
  }'
```

**Data Signing Response:**

```json theme={null}
{
  "data": {
    "messageHash": "0xabc...",
    "signature": "0xabc...",
    "r": "",
    "s": "",
    "v": ""
  }
}
```

#### Request Parameters

<ParamField body="op_jwt" type="string" required post={["Body"]}>
  Short-lived JWT (max 5 minutes) for the user performing the operation.
</ParamField>

<ParamField body="wallet_id" type="string" required post={["Body"]}>
  The UUID of the wallet to sign with.
</ParamField>

<ParamField body="access_key" type="string" required post={["Body"]}>
  The access key returned from wallet creation. Retrieve from your database.
</ParamField>

<ParamField body="raw_data_hash" type="string" required post={["Body"]}>
  The keccak256 hash of the data to sign.
</ParamField>

#### Response Fields

<ResponseField name="data.messageHash" type="string">
  The hash that was signed.
</ResponseField>

<ResponseField name="data.signature" type="string">
  The complete signature in hex format.
</ResponseField>

<ResponseField name="data.r" type="string">
  The r component of the ECDSA signature.
</ResponseField>

<ResponseField name="data.s" type="string">
  The s component of the ECDSA signature.
</ResponseField>

<ResponseField name="data.v" type="string">
  The recovery parameter of the ECDSA signature.
</ResponseField>

### Sign Message (Solana)

Sign an arbitrary message using a Solana wallet.

```bash cURL icon="square-terminal" theme={null}
curl -X POST 'https://tee.magiclabs.com/v2/api/wallet/sign_message' \
  -H 'Content-Type: application/json' \
  -H 'x-magic-secret-key: sk_live_XXXXXXXX' \
  -d '{
    "op_jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGgueW91cmFwcC5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vdGVlLm1hZ2ljbGFicy5jb20iLCJpYXQiOjE3NTE2NzIwMDAsImV4cCI6MTc1MTY3MjMwMH0.SIGNATURE",
    "wallet_id": "e982b4a3-14d3-4d66-a3ac-fadfc3xxxxxx",
    "access_key": "key_shard",
    "message_base64": "base64_string_hash"
  }'
```

**Solana Message Signing Response:**

```json theme={null}
{
  "data": {
    "signature": "base64_encoded"
  }
}
```

#### Request Parameters

<ParamField body="op_jwt" type="string" required post={["Body"]}>
  Short-lived JWT (max 5 minutes) for the user performing the operation.
</ParamField>

<ParamField body="wallet_id" type="string" required post={["Body"]}>
  The UUID of the wallet to sign with.
</ParamField>

<ParamField body="access_key" type="string" required post={["Body"]}>
  The access key returned from wallet creation. Retrieve from your database.
</ParamField>

<ParamField body="message_base64" type="string" required post={["Body"]}>
  The message to be signed, encoded in base64.
</ParamField>

#### Response Fields

<ResponseField name="data.signature" type="string">
  The signature in base64 format.
</ResponseField>

## v1 Wallet Migration

If you have existing v1 wallets, they are migrated to v2 on first use. Include `encryption_context` alongside `op_jwt` and `access_key` on the first v2 operation for that wallet. The enclave will re-key the wallet to v2 JWT binding as a side effect, and subsequent operations will not require `encryption_context`.

<ParamField body="encryption_context" type="string" post={["Body"]}>
  Required only when migrating a v1 wallet on first v2 use. The original encryption context used during v1 wallet creation.
</ParamField>

<Info>
  After the first successful v2 operation, the wallet is permanently migrated and `encryption_context` is no longer accepted or needed.
</Info>
