Create Your First Wallet with Email
Use API requests and Circle’s sample app to create a user-controlled wallet with email OTP
Learn how to initialize and create a user-controlled wallet using the email. To create a wallet with social logins, see Create Your First Wallet with Social Logins.
This quickstart utilizes Circle’s sample application in combination with API requests that can be sent using cURL requests or Circle's API references. cURL requests are provided inline while API references call the API endpoints. For more on how to use API references to make calls, see Testing API References.
You can create both Smart Contract Accounts (SCA) and Externally Owned Accounts (EOA) wallets. To learn more, see the Account Type guide.
Prerequisites
Before you begin:
- Create or sign in to your Circle Developer Console account.
- Generate an API Key.
- Complete the Authentication Methods configurations before you set up the sample app.
This guide walks you through steps on how to create a wallet and perform transactions or signatures, and provides sample code. You can use Circle's sample app on web or set it up locally.
You can view the sample app source code on GitHub:
If you want to test in the iOS or Android environment, you can check our Github repo for iOS sample app and Android sample app. React Native sample app is coming soon.
Step 1. Configure the sample app
- Select the Email tab.
- Obtain an App ID. Either:
- From the Circle Developer Console, navigate to Programmable Wallets > User Controlled > Configurator and copy App ID.
- Send a
GET
request to the/config/entity
endpoint and copy theappId
from the response body.
- Add the App ID to the sample app.
The sample app generates and pre-populates the device ID. During actual implementation, you must retrieve it by calling the SDK method
getDeviceId
.
View of the web sample app hosted by Circle
Step 2. Configure SMTP settings and customize one-time passcode (OTP) email
- From the Circle Developer Console, navigate to Programmable Wallets > User Controlled > Configurator.
- Select Authentication Methods > Email.
- Enter your SMTP data.
- Customize your OTP email content:
- Enter a From email address and Subject for the email’s subject line.
- Modify the content in the Email Template.
Step 3. Perform email login
-
Include your API key,
deviceId
, and email address in aPOST
request to the/users/email/token
endpoint.curl --location 'https://api.circle.com/v1/w3s/users/email/token' \ --header 'Content-Type: application/json' \ --header `Authorization: Bearer ${your api key}` \ --data '{ "idempotencyKey": "3eeacdee-786c-4777-854d-6e457a060782", "deviceId": "your device id" }'
-
Copy
deviceToken
,deviceEncryptionKey
, andotpToken
from the response and enter them into the sample app.{ deviceToken: string deviceEncryptionKey: string otpToken?: string // For email authentication method only }
-
On the sample app, select Login with Email. This takes you through the OTP email flow:
-
An email containing an OTP is sent to the email address specified in your request to
/users/email/token
. -
The sample app prompts you with a UI to enter the OTP to verify identity, which corresponds to the SDK method
verifyOTP
.
-
-
Once the OTP is verified, you are redirected back to the main page of the sample app. The "Execute Challenge" section is now visible.
-
Select Execute Challenge.
BothencryptionKey
anduserToken
are pre-populated since these parameters are required for the next step, which is to initialize the user.
The sample app pre-populates the
encryptionKey
anduserToken
for you. During the actual development, the client-side SDK returnsuserId
,userToken
,encryptionKey
, andrefreshToken
to you.
Step 4. Initialize user and acquire challenge ID
- Include
userToken
copied from the previous step in aPOST
request to the/user/initialize
endpoint. - Copy
challengeId
from the response and enter it into the sample app.
To create an SCA wallet, provide a Testnet blockchain such as ETH-SEPOLIA, MATIC-AMOY, and AVAX-FUJI.
Amoy example
The following code samples show how to create an SCA wallet on Amoy and the response.
const response = await circleUserSdk.createUserPinWithWallets({
userToken: '<USER_TOKEN>',
accountType: 'SCA',
blockchains: ['MATIC-AMOY']
});
# create an api instance
api_instance = user_controlled_wallets.UsersAndPinsApi(client)
try:
request = user_controlled_wallets.SetPinAndInitWalletRequest.from_dict({"accountType": 'SCA', "blockchains": ['MATIC-AMOY'], "idempotencyKey": str(uuid.uuid4()) })
response = api_instance.create_user_with_pin_challenge("<USER_TOKEN>", request)
print(response)
except user_controlled_wallets.ApiException as e:
print("Exception when calling UsersAndPinsApi->create_user_with_pin_challenge: %s\n" % e)
curl --request POST \
--url 'https://api.circle.com/v1/w3s/user/initialize' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'X-User-Token: <USER_TOKEN>' \
--data '
{
"idempotencyKey": "49e3f455-60a2-4b5e-9e9e-9400b86e5f34",
"accountType": "SCA",
"blockchains": [
"MATIC-MUMBAI"
]
}
'
{
"data": {
"challengeId": "0d1b5f41-1381-50af-983b-f54691415158"
}
}
Solana example
The following code samples show how to create an EOA wallet on Solana and the response.
const response = await circleUserSdk.createUserPinWithWallets({
userToken: '<USER_TOKEN>',
accountType: 'EOA',
blockchains: ['SOL-DEVNET']
});
# create an api instance
api_instance = user_controlled_wallets.UsersAndPinsApi(client)
try:
request = user_controlled_wallets.SetPinAndInitWalletRequest.from_dict({"accountType": 'EOA', "blockchains": ['SOL-DEVNET'], "idempotencyKey": str(uuid.uuid4()) })
response = api_instance.create_user_with_pin_challenge("<USER_TOKEN>", request)
print(response)
except user_controlled_wallets.ApiException as e:
print("Exception when calling UsersAndPinsApi->create_user_with_pin_challenge: %s\n" % e)
curl --request POST \
--url 'https://api.circle.com/v1/w3s/user/initialize' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'X-User-Token: <USER_TOKEN>' \
--data '
{
"idempotencyKey": "a6f4c8d2-33b1-43b0-8a46-5b14efe063d8",
"accountType": "EOA",
"blockchains": [
"SOL-DEVNET"
]
}
'
{
"data": {
"challengeId": "0d1b5f41-1381-50af-983b-f54691415158"
}
}
Step 5. Create wallet
- Paste the Challenge ID copied from the previous step into the sample app, and select Execute.
- An “Execute Successful” message is displayed on the sample app. A web3 wallet is created for you users!
To execute a challenge during actual implementation, you must call the Web SDK API
execute
with thechallengeId
returned from Circle. Also, make sure you have an activeuserToken
,encryptionKey
for any challenge executions.
Step 6. Check user and wallet status
Once you have created a wallet in the sample app, you can check the user and wallet status.
To check the user's account status:
- Include
userToken
in aGET
request to the/user
endpoint to retrieve the status of the user’s account.
const response = await circleUserSdk.getUserStatus({
userToken: '<USER_TOKEN>'
});
# create an api instance
api_instance = user_controlled_wallets.UsersAndPinsApi(client)
# get user by token
try:
response = api_instance.get_user_by_token("<USER_TOKEN>")
print(response)
except user_controlled_wallets.ApiException as e:
print("Exception when calling UsersAndPinsApi->get_user_by_token: %s\n" % e)
curl --request GET \
--url 'https://api.circle.com/v1/w3s/user' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'X-User-Token: <USER_TOKEN>'
{
"data": {
"id": "2f1dcb5e-312a-4b15-8240-abeffc0e3463",
"status": "ENABLED",
"createDate": "2023-07-26T15:27:32Z",
"pinStatus": "ENABLED",
"pinDetails": {
"failedAttempts": 0
},
"securityQuestionStatus": "ENABLED",
"securityQuestionDetails": {
"failedAttempts": 0
}
}
}
To check the status of the user’s new wallet.
- Include
userToken
in aGET
request to the/wallets
endpoint to retrieve the user’s new wallet.
const response = await circleUserSdk.listWallets({
userToken: '<USER_TOKEN>'
});
# create an api instance
api_instance = user_controlled_wallets.WalletsApi(client)
# get user token
try:
response = api_instance.list_wallets("<USER_TOKEN>")
print(response)
except user_controlled_wallets.ApiException as e:
print("Exception when calling WalletsApi->list_wallets: %s\n" % e)
curl --request GET \
--url 'https://api.circle.com/v1/w3s/wallets' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'X-User-Token: <USER_TOKEN>'
Amoy sample response
{
"data": {
"wallets": [
{
"id": "01899cf2-d415-7052-a207-f9862157e546",
"state": "LIVE",
"walletSetId": "01899cf2-d407-7f89-b4d9-84d63573f138",
"custodyType": "ENDUSER",
"userId": "2f1dcb5e-312a-4b15-8240-abeffc0e3463",
"address": "0x075e62c80e55d024cfd8fd4e3d1184834461db57",
"addressIndex": 0,
"blockchain": "MATIC-AMOY",
"accountType": "SCA",
"updateDate": "2023-07-28T14:41:47Z",
"createDate": "2023-07-28T14:41:47Z"
}
]
}
}
Solana sample response
{
"data": {
"wallets": [
{
"id": "8a79c80b-4d4f-4032-971a-8bb9f9b0254f",
"state": "LIVE",
"walletSetId": "c43221d3-9db1-4cbf-8b18-e1dcae16b55d",
"custodyType": "ENDUSER",
"userId": "d8c8f832-5d4f-4123-9a7f-60120c2da5f0",
"address": "8UFfxP3zzSeqdkZ5iLTmUGzpHPRGnydZ1Vnq5GkzKTep",
"addressIndex": 0,
"blockchain": "SOL-DEVNET",
"accountType": "EOA",
"updateDate": "2023-07-28T14:43:48Z",
"createDate": "2023-07-28T14:43:48Z"
}
]
}
}
You can also view the User ID, Auth Method, and Wallet status on the Circle Developer Console:
- From the Programmable Wallets section on the sidebar, select User Controlled > Users.
- Select your user from the row. The wallet address is displayed.
Updated 10 days ago