SIWE Authentication
Prepare and complete the manual SIWE flow for protected Namefi API operations
SIWE (EIP-4361) gives you a session token after one ERC-191 personal_sign signature. Use it for protected operations that do not require EIP-712.
At the moment, production SIWE has a known server-side issue in prepareSiweMessage caused by an invalid URI value. If you see a 500 INTERNAL_SERVER_ERROR during message preparation on production, that is a backend bug rather than a signing-flow bug in your client or tooling.
Flow
GET /v-next/siwe/allowed-chainsGET /v-next/siwe/nonce?signerAddress=...GET /v-next/siwe/message?signerAddress=...&nonce=...&chainId=...- Sign
messageStringwithpersonal_sign POST /v-next/siwe/verify- Send the returned token as
x-namefi-siwe-token
Step 1: Allowed chains
const allowedChains = await fetch(
'https://api.namefi.io/v-next/siwe/allowed-chains',
).then((r) => r.json());
console.log(allowedChains);
// Production: [1, 8453]
// Development: [11155111, 46630]Pick one of the returned chain IDs for the SIWE message.
Step 2: Get a nonce
const { valid, nonce } = await fetch(
'https://api.namefi.io/v-next/siwe/nonce?signerAddress=0xYourAddress',
).then((r) => r.json());
if (!valid) {
throw new Error('Failed to get nonce');
}The nonce is single-use and expires after 5 minutes.
Step 3: Prepare the message
const { valid, message, messageString } = await fetch(
`https://api.namefi.io/v-next/siwe/message?signerAddress=0xYourAddress&nonce=${nonce}&chainId=1`,
).then((r) => r.json());
if (!valid) {
throw new Error('Failed to prepare SIWE message');
}message is the structured payload you send back to /siwe/verify.
messageString is the exact ERC-191 string you sign with personal_sign.
Step 4: Sign with ERC-191
The signature step is external to the API and external to the namefi-api skill.
viem
const signature = await account.signMessage({ message: messageString });WalletConnect
const signature = await provider.request({
method: 'personal_sign',
params: [messageString, '0xYourAddress'],
});ethers.js
const signature = await signer.signMessage(messageString);Step 5: Verify and get a token
const verifyResult = await fetch('https://api.namefi.io/v-next/siwe/verify', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
address: '0xYourAddress',
message,
signature,
}),
}).then((r) => r.json());
if (!verifyResult.valid) {
throw new Error(`Verification failed: ${verifyResult.error}`);
}
const token = verifyResult.token;Successful responses also include session details such as chainId, createdAt, and maxAgeSeconds.
Step 6: Call the protected operation
const domains = await fetch('https://api.namefi.io/v-next/user/domains', {
headers: {
'x-namefi-siwe-token': token,
},
}).then((r) => r.json());Session details
| Property | Value |
|---|---|
| Nonce lifetime | 5 minutes (300 seconds) |
| Token lifetime | 12 hours (43200 seconds) |
| Token header | x-namefi-siwe-token |
| Signing method | ERC-191 personal_sign |
Using the skill helpers
The public namefi-skills repo exposes two useful prep flows:
Full operation-aware prep
bun .rulesync/skills/namefi-api/scripts/prepare-auth-request.ts \
--env dev \
--operationId getUserDomains \
--signer-address 0xYourAddressThat returns:
- allowed chains
- selected chain ID
- nonce
- structured SIWE message
messageString- a verify request template
- a final request template with
x-namefi-siwe-token
SIWE-only helper
bun .rulesync/skills/namefi-api/scripts/prepare-siwe-message.ts \
--env dev \
--signer-address 0xYourAddressUse either output with any external signer or MCP that can do personal_sign.
For the combined operation-aware entrypoint that also handles EIP-712 and no-auth cases, see Prepare Auth Requests.
Client-library behavior
When you use @namefi/api-client with type: 'EIP712', the client performs this SIWE flow automatically for siwe-required and siwe-optional operations.
For raw integrations, the flow stays the same: prepare the message, sign it externally, verify it, then reuse the token until it expires.