Request a cryptographic signature from a user’s wallet without broadcasting a transaction. Signatures prove the user controls the wallet and let your app verify offchain actions like Sign-In With Ethereum (SIWE) authentication, EIP-712 typed-data approvals (Permit2, DEX orders, marketplace listings), and arbitrary text messages signed with EIP-191. Signing is offchain and doesn’t cost gas. For onchain transactions, use Transfer Tokens instead.Documentation Index
Fetch the complete documentation index at: https://developers.circle.com/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
Before you begin, ensure that you’ve:- Obtained a Circle Developer API key from the Circle Console.
- Completed the
Build a Wallet App tutorial,
which sets up a user-controlled wallet and stores the user’s
userId. - Integrated a user-controlled wallet client-side SDK in your app to walk the user through the signature challenge: Web SDK, iOS SDK, Android SDK, or React Native SDK.
- Installed the user-controlled wallet server-side SDK in your backend to create the signature challenge: Node.js or Python.
Steps
Create the signature challenge
Call the SDK method that matches your signature type. Each returns a
Include an
challengeId that the user authorizes in the next step.- Personal sign (EIP-191)
- Typed data (EIP-712)
- Transaction object
Sign an arbitrary text message. Useful for SIWE authentication challenges.
idempotencyKey (a UUID) on every signing call. Retrying with the
same key prevents duplicate challenges. See
Idempotent requests for details on
idempotency key usage.Have the user authorize the signature
Pass the
userToken, encryptionKey, and challengeId to your client-side
SDK. The SDK presents the signing details and the appropriate authorization UI:- Social login or email OTP: Circle displays a confirmation UI showing the message or typed data. See Confirmation UIs to customize or replace it.
- PIN: The user enters their PIN (or uses biometrics) to authorize.
Fetch the completed signature
Wait for the challenge to complete, then retrieve the signature. Use webhooks
(push) or polling (pull) to detect when the challenge reaches a terminal status:
For a full list of possible statuses, see
Asynchronous States and Statuses.
COMPLETED, FAILED, or EXPIRED.- Webhook
- Polling
Subscribe to user challenge notifications and listen for the event matching your
For webhook setup, see Webhook Notifications.
challengeId. The notification includes the challenge status, type
(SIGN_MESSAGE, SIGN_TYPEDDATA, or SIGN_TRANSACTION), and the signed
output.Webhook notification
Error handling
Handle these common failure cases when integrating signature requests:- Expired session token (error code
155104): TheuserTokenexpires after 60 minutes. Request a new session token and retry. - Invalid typed data: Malformed EIP-712 structures fail before reaching the
user. Validate the
domain,types, andmessageagainst the EIP-712 spec before initiating the challenge. - User declines or fails to authorize: If the user cancels or enters an incorrect PIN, the signature isn’t generated. Surface the cancellation and let them retry.
- Signature verification fails in your backend: If the recovered address doesn’t match the wallet, the user likely signed a different message than what you’re verifying. Make sure the message your backend verifies matches exactly what was signed, including whitespace and encoding.