> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Sign-In with Ethereum

Wallet-based authentication (SIWE): nonce and verify to sign in, and link or unlink additional wallets.

## Initialize SIWE login

`POST /iam/v2/auth/siwe/nonce`

Generate a nonce for Sign-In With Ethereum (SIWE) authentication

### Request body (required) (`application/json`)

- `walletAddress` `string` _(required)_: Ethereum wallet address (must be 42 characters starting with 0x)
- `chainId` `number`: Blockchain network chain ID (default: 1 for Ethereum mainnet)

### Responses

#### `200`: Nonce generated successfully

Body (`application/json`):

- `nonce` `string` _(required)_: Cryptographically secure random nonce to be used in SIWE message

#### `400`: Bad Request. Invalid wallet address format or parameters.

Body (`application/json`):

- `message` `string` _(required)_

#### `401`: Unauthorized. Due to missing or invalid authentication.

Body (`application/json`):

- `message` `string` _(required)_

#### `500`: Internal Server Error. This is a problem with the server that you cannot fix.

Body (`application/json`):

- `message` `string`

### Example request

```bash
curl https://api.openfort.io/iam/v2/auth/siwe/nonce \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "walletAddress": "string",
  "chainId": 1
}'
```

```ts
fetch('https://api.openfort.io/iam/v2/auth/siwe/nonce', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    walletAddress: 'string',
    chainId: 1
  })
})
```

## Login with SIWE

`POST /iam/v2/auth/siwe/verify`

Verify a signed SIWE message and authenticate the user

### Request body (required) (`application/json`)

- `message` `string` _(required)_: The SIWE message string that was signed
- `signature` `string` _(required)_: The signature from the user's wallet
- `walletAddress` `string` _(required)_: Ethereum wallet address (must be 42 characters starting with 0x)
- `chainId` `number`: Blockchain network chain ID (must match the Chain ID in SIWE message, default: 1)
- `walletClientType` `string`: Type of wallet client used (e.g., 'Phantom', 'MetaMask')
- `connectorType` `string`: Type of connector used (e.g., 'injected', 'walletConnect')

### Responses

#### `200`: Authentication successful

Body (`application/json`):

- `token` `string` _(required)_: Session token
- `success` `boolean` _(required)_: Always true on successful authentication
- `user` `object` _(required)_
  - `id` `string` _(required)_: User ID
  - `walletAddress` `string` _(required)_: Ethereum wallet address
  - `chainId` `number` _(required)_: Chain ID used for authentication

#### `400`: Bad Request. Invalid parameters or email required when anonymous is disabled.

Body (`application/json`):

- `message` `string` _(required)_

#### `401`: Unauthorized. Invalid or expired nonce, invalid signature, or chainId mismatch.

Body (`application/json`):

- `message` `string` _(required)_
- `code` `string`: Error code (e.g., UNAUTHORIZED_INVALID_OR_EXPIRED_NONCE)

#### `500`: Internal Server Error. This is a problem with the server that you cannot fix.

Body (`application/json`):

- `message` `string`

### Example request

```bash
curl https://api.openfort.io/iam/v2/auth/siwe/verify \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "message": "string",
  "signature": "string",
  "walletAddress": "string",
  "chainId": 1,
  "walletClientType": "string",
  "connectorType": "string"
}'
```

```ts
fetch('https://api.openfort.io/iam/v2/auth/siwe/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    message: 'string',
    signature: 'string',
    walletAddress: 'string',
    chainId: 1,
    walletClientType: 'string',
    connectorType: 'string'
  })
})
```

## Initialize SIWE link

`POST /iam/v2/auth/link-siwe/nonce`

Generates a cryptographically secure nonce for creating a SIWE message to link a wallet to the current authenticated user. Requires active session.

### Request body (required) (`application/json`)

- `walletAddress` `string` _(required)_: Ethereum wallet address (must be 42 characters starting with 0x)
- `chainId` `number`: Blockchain network chain ID (default: 1 for Ethereum mainnet)

### Responses

#### `200`: Nonce generated successfully

Body (`application/json`):

- `nonce` `string` _(required)_: Cryptographically secure nonce to use in SIWE message

#### `400`: Bad Request. Invalid wallet address format or parameters.

Body (`application/json`):

- `message` `string` _(required)_

#### `401`: Unauthorized. User must be logged in to link a wallet.

Body (`application/json`):

- `message` `string` _(required)_

#### `500`: Internal Server Error.

Body (`application/json`):

- `message` `string`

### Example request

```bash
curl https://api.openfort.io/iam/v2/auth/link-siwe/nonce \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "walletAddress": "string",
  "chainId": 1
}'
```

```ts
fetch('https://api.openfort.io/iam/v2/auth/link-siwe/nonce', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    walletAddress: 'string',
    chainId: 1
  })
})
```

## Verify and link SIWE wallet

`POST /iam/v2/auth/link-siwe/verify`

Verifies the SIWE signature and links the wallet to the currently authenticated user. Requires active session.

### Request body (required) (`application/json`)

- `message` `string` _(required)_: The SIWE message string that was signed
- `signature` `string` _(required)_: The signature from the user's wallet
- `walletAddress` `string` _(required)_: Ethereum wallet address (must be 42 characters starting with 0x)
- `chainId` `number`: Blockchain network chain ID (must match the Chain ID in SIWE message, default: 1)
- `walletClientType` `string`: Type of wallet client used (e.g., 'Phantom', 'MetaMask')
- `connectorType` `string`: Type of connector used (e.g., 'injected', 'walletConnect')

### Responses

#### `200`: Wallet linked successfully

Body (`application/json`):

- `success` `boolean` _(required)_: Whether the wallet was successfully linked
- `walletAddress` `string` _(required)_: The checksummed Ethereum address that was linked
- `chainId` `number` _(required)_: The blockchain network chain ID
- `isPrimary` `boolean` _(required)_: Whether this is the user's primary wallet (first wallet linked)
- `connectorType` `string`: Type of connector used (e.g., 'injected', 'walletConnect')
- `walletClientType` `string`: Type of wallet client used (e.g., 'Phantom', 'MetaMask')
- `ensName` `string`: ENS name associated with the wallet (if available)
- `ensAvatar` `string`: ENS avatar URL (if available)
- `message` `string`: Optional informational message

#### `400`: Bad Request. Wallet already linked to another account or invalid parameters.

Body (`application/json`):

- `message` `string` _(required)_
- `code` `string`: Error code indicating the type of error

#### `401`: Unauthorized. User not logged in, invalid/expired nonce, or invalid signature.

Body (`application/json`):

- `message` `string` _(required)_
- `code` `string`: Error code indicating the authentication failure reason

#### `500`: Internal Server Error. Failed to link wallet.

Body (`application/json`):

- `message` `string`

### Example request

```bash
curl https://api.openfort.io/iam/v2/auth/link-siwe/verify \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "message": "string",
  "signature": "string",
  "walletAddress": "string",
  "chainId": 1,
  "walletClientType": "string",
  "connectorType": "string"
}'
```

```ts
fetch('https://api.openfort.io/iam/v2/auth/link-siwe/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    message: 'string',
    signature: 'string',
    walletAddress: 'string',
    chainId: 1,
    walletClientType: 'string',
    connectorType: 'string'
  })
})
```

## List linked wallets

`GET /iam/v2/auth/link-siwe/list-wallets`

Get all SIWE wallets linked to the authenticated user with full wallet metadata including primary status and chain information. Requires active session.

### Responses

#### `200`: Wallets retrieved successfully

Body (`application/json`):

- `id` `string` _(required)_: Unique identifier for this wallet record
- `userId` `string` _(required)_: User ID this wallet is linked to
- `address` `string` _(required)_: Checksummed Ethereum wallet address
- `chainId` `number` _(required)_: Blockchain network chain ID
- `isPrimary` `boolean` _(required)_: Whether this is the user's primary wallet
- `createdAt` `string <date-time>` _(required)_: Timestamp when the wallet was linked

#### `401`: Unauthorized. User is not authenticated.

Body (`application/json`):

- `message` `string`

#### `500`: Internal Server Error.

Body (`application/json`):

- `message` `string`

### Example request

```bash
curl https://api.openfort.io/iam/v2/auth/link-siwe/list-wallets
```

```ts
fetch('https://api.openfort.io/iam/v2/auth/link-siwe/list-wallets')
```

## Unlink SIWE wallet

`POST /iam/v2/auth/link-siwe/unlink`

Remove a linked wallet from the authenticated user account. If the wallet being unlinked is the primary wallet, another wallet will be automatically promoted to primary. Requires active session.

### Request body (required) (`application/json`)

- `walletAddress` `string` _(required)_: Ethereum wallet address to unlink (must be 42 characters starting with 0x)
- `chainId` `number`: Blockchain network chain ID (default: 1 for Ethereum mainnet)

### Responses

#### `200`: Wallet unlinked successfully

Body (`application/json`):

- `success` `boolean` _(required)_: Indicates successful wallet unlinking

#### `400`: Bad Request. Cannot unlink last authentication method, wallet not found, or invalid parameters.

Body (`application/json`):

- `message` `string` _(required)_
- `code` `string`: Error code indicating the type of error

#### `401`: Unauthorized. User is not authenticated.

Body (`application/json`):

- `message` `string`

#### `500`: Internal Server Error. Failed to unlink wallet.

Body (`application/json`):

- `message` `string`

### Example request

```bash
curl https://api.openfort.io/iam/v2/auth/link-siwe/unlink \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "walletAddress": "string",
  "chainId": 1
}'
```

```ts
fetch('https://api.openfort.io/iam/v2/auth/link-siwe/unlink', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    walletAddress: 'string',
    chainId: 1
  })
})
```
