cURL
curl --request POST \
--url https://grid.squads.xyz/api/grid/v1/accounts/verify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "<string>",
"kms_provider_config": {
"encryption_public_key": "<string>"
},
"otp_code": "<string>"
}
'import requests
url = "https://grid.squads.xyz/api/grid/v1/accounts/verify"
payload = {
"email": "<string>",
"kms_provider_config": { "encryption_public_key": "<string>" },
"otp_code": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: '<string>',
kms_provider_config: {encryption_public_key: '<string>'},
otp_code: '<string>'
})
};
fetch('https://grid.squads.xyz/api/grid/v1/accounts/verify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://grid.squads.xyz/api/grid/v1/accounts/verify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'kms_provider_config' => [
'encryption_public_key' => '<string>'
],
'otp_code' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://grid.squads.xyz/api/grid/v1/accounts/verify"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"kms_provider_config\": {\n \"encryption_public_key\": \"<string>\"\n },\n \"otp_code\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://grid.squads.xyz/api/grid/v1/accounts/verify")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"kms_provider_config\": {\n \"encryption_public_key\": \"<string>\"\n },\n \"otp_code\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://grid.squads.xyz/api/grid/v1/accounts/verify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"kms_provider_config\": {\n \"encryption_public_key\": \"<string>\"\n },\n \"otp_code\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"address": "<string>",
"authentication": [
{
"session": {
"Privy": {
"privy_access_token": "<string>",
"refresh_token": "<string>",
"session": {
"expires_at": 1,
"wallets": [
{
"additional_signers": [
{
"signer_id": "<string>",
"override_policy_ids": [
"<string>"
]
}
],
"address": "<string>",
"created_at": 1,
"id": "<string>",
"policy_ids": [
"<string>"
],
"exported_at": 1,
"imported_at": 1,
"owner_id": "<string>",
"public_key": "<string>"
}
],
"authorization_key": "<string>",
"encrypted_authorization_key": {
"ciphertext": "<string>",
"encapsulated_key": "<string>",
"encryption_type": "<string>"
}
},
"token": "<string>",
"user_id": "<string>"
}
}
}
],
"grid_user_id": "<string>",
"policies": {
"signers": [
{
"address": "<string>",
"permissions": []
}
],
"threshold": 1,
"admin_address": "<string>",
"time_lock": 1
},
"status": "<string>"
},
"metadata": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}Account Management
Verify Account OTP
Verify OTP for email-based account creation and complete account setup
POST
/
api
/
grid
/
v1
/
accounts
/
verify
cURL
curl --request POST \
--url https://grid.squads.xyz/api/grid/v1/accounts/verify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "<string>",
"kms_provider_config": {
"encryption_public_key": "<string>"
},
"otp_code": "<string>"
}
'import requests
url = "https://grid.squads.xyz/api/grid/v1/accounts/verify"
payload = {
"email": "<string>",
"kms_provider_config": { "encryption_public_key": "<string>" },
"otp_code": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: '<string>',
kms_provider_config: {encryption_public_key: '<string>'},
otp_code: '<string>'
})
};
fetch('https://grid.squads.xyz/api/grid/v1/accounts/verify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://grid.squads.xyz/api/grid/v1/accounts/verify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'kms_provider_config' => [
'encryption_public_key' => '<string>'
],
'otp_code' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://grid.squads.xyz/api/grid/v1/accounts/verify"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"kms_provider_config\": {\n \"encryption_public_key\": \"<string>\"\n },\n \"otp_code\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://grid.squads.xyz/api/grid/v1/accounts/verify")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"kms_provider_config\": {\n \"encryption_public_key\": \"<string>\"\n },\n \"otp_code\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://grid.squads.xyz/api/grid/v1/accounts/verify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"kms_provider_config\": {\n \"encryption_public_key\": \"<string>\"\n },\n \"otp_code\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"address": "<string>",
"authentication": [
{
"session": {
"Privy": {
"privy_access_token": "<string>",
"refresh_token": "<string>",
"session": {
"expires_at": 1,
"wallets": [
{
"additional_signers": [
{
"signer_id": "<string>",
"override_policy_ids": [
"<string>"
]
}
],
"address": "<string>",
"created_at": 1,
"id": "<string>",
"policy_ids": [
"<string>"
],
"exported_at": 1,
"imported_at": 1,
"owner_id": "<string>",
"public_key": "<string>"
}
],
"authorization_key": "<string>",
"encrypted_authorization_key": {
"ciphertext": "<string>",
"encapsulated_key": "<string>",
"encryption_type": "<string>"
}
},
"token": "<string>",
"user_id": "<string>"
}
}
}
],
"grid_user_id": "<string>",
"policies": {
"signers": [
{
"address": "<string>",
"permissions": []
}
],
"threshold": 1,
"admin_address": "<string>",
"time_lock": 1
},
"status": "<string>"
},
"metadata": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}This endpoint verifies the OTP received via email and completes the account creation process by deploying a Grid Account on the Solana blockchain with TEE-encrypted authorization keys.
Using the Grid API directly requires advanced configurations. Grid SDK is
the recommended way to create accounts. It handles account creation, key
management, authentication, automatic failover, and transaction signing. Learn
more about the Grid SDK in the Grid SDK guide.
This is step 2 of the email-based Grid Account creation flow. You must have
called the Create
Account endpoint to
initiate account creation before calling this endpoint.
OTP Limits
- Attempts: Maximum 3 verification attempts per OTP
- Expiration: 15-minute window from account creation
- Retry: Must initiate new account creation if limits exceeded
Required Configuration
When using Privy as the authentication provider (default), you must include a kms_provider_config with your HPKE public key to receive encrypted authorization keys. This enables secure transaction signing for your Grid Account.Complete Implementation Guide
For comprehensive implementation details including:- kms_provider_config creation
- HPKE keypair generation with P-256 curve and DER formatting
- Authorization key decryption using ECDH + HKDF + ChaCha20-Poly1305
- Transaction payload signing with JSON canonicalization
- Error handling and security best practices
- Language-agnostic examples
Authorizations
Your Grid API key from the Grid Dashboard
Body
application/json
Was this page helpful?
⌘I