Wallet Verification Results and Attestations

Verification states, the verification and attestation objects, how to re-check a signature yourself, and the signed message format.

When a user signs, Wallet Verification checks the signature and stores an attestation on the verification. The attestation holds everything you need to re-check the proof independently.

Verification states

StateTerminalMeaning
pendingNoCreated. Waiting for the user to sign.
verifiedYesThe signature checked out. attestation is set.
failedYesThe signature did not verify. failure_reason is set.
expiredYesexpires_at passed without a signature.
cancelledYesYou cancelled the verification. The link no longer works.

failure_reason is currently always invalid_signature.

Verification object

FieldDescription
idVerification ID, ver_ followed by 32 hex characters.
stateOne of the states above.
reference_idYour identifier from create.
purposewithdrawal, payout, onboarding, or re_verification.
chain_idCAIP-2 chain ID you pinned, or null.
expected_addressAddress you pinned, or null.
expires_atWhen the link stops working.
return_urlReturn URL you set, or null.
created_atWhen the verification was created.
metadataYour metadata object, echoed back. Never signed.
failure_reasonSet when state is failed.
attestationSet when state is verified. See below.

The create response also includes url. No other response returns it.

Attestation object

FieldDescription
addressVerified address. EIP-55 checksummed for EVM, base58 for Solana.
chain_idCAIP-2 chain ID the user signed on.
messageThe exact text the user signed.
signature0x hex for EVM, base58 for Solana.
wallet_typeeoa, eoa_7702, contract, or counterfactual.
verification_methodecrecover, erc1271, erc6492, or ed25519.
block_numberBlock the contract check ran at, as a decimal string. null for stateless methods.
block_hashHash of that block. null for stateless methods.
verified_atWhen the signature was verified.

ecrecover and ed25519 are stateless. erc1271 and erc6492 call the wallet contract, so they record the block.

Attestations are kept for 5 years.

Verify an attestation yourself

You can re-check any attestation without trusting Wallet Verification. Pick the check that matches verification_method.

These examples are illustrative. Use them as a starting point and adapt chain selection, RPC endpoints, and error handling to your stack.

Recover the EIP-191 personal_sign signer over message and compare it to address.

verify-ecrecover.ts
import { verifyMessage } from "viem";

const valid = await verifyMessage({
  address: attestation.address,
  message: attestation.message,
  signature: attestation.signature,
});

Signed message

EVM wallets sign an EIP-4361 (Sign-In with Ethereum) message. Solana wallets sign the equivalent CAIP-122 message.

Signed message format
{page host} wants you to sign in with your {Ethereum|Solana} account:
{address}

{Requester name} asks you to verify wallet ownership to {purpose text}.

URI: {page origin}/s
Version: 1
Chain ID: {EVM: chain number, e.g. 1 | Solana: full CAIP-2 id}
Nonce: {nonce}
Issued At: {created_at}
Expiration Time: {expires_at}
Request ID: {verification id, ver_…}
PurposePurpose text
withdrawalauthorise a withdrawal to this wallet
payoutreceive payouts at this wallet
onboardingcomplete onboarding
re_verificationre-confirm ownership of this wallet

The requester name is the business name from Customization, with accents stripped to fit the format. Without one, it reads "The requester". Your reference_id and metadata are not part of the message.

To fetch only the signed text, call GET /verifications/{id}/message. It returns text/plain, or 409 not_verified if the verification is not verified.

On this page