cURL
curl --request POST \
--url https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'x-grid-environment: <x-grid-environment>' \
--data '
{
"signer": "<string>",
"transaction": "<string>",
"type": "custom",
"fee_config": {
"payer_address": "<string>",
"self_managed_fees": true
}
}
'import requests
url = "https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals"
payload = {
"signer": "<string>",
"transaction": "<string>",
"type": "custom",
"fee_config": {
"payer_address": "<string>",
"self_managed_fees": True
}
}
headers = {
"x-grid-environment": "<x-grid-environment>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-grid-environment': '<x-grid-environment>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
signer: '<string>',
transaction: '<string>',
type: 'custom',
fee_config: {payer_address: '<string>', self_managed_fees: true}
})
};
fetch('https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals', 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/{address}/proposals",
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([
'signer' => '<string>',
'transaction' => '<string>',
'type' => 'custom',
'fee_config' => [
'payer_address' => '<string>',
'self_managed_fees' => true
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"x-grid-environment: <x-grid-environment>"
],
]);
$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/{address}/proposals"
payload := strings.NewReader("{\n \"signer\": \"<string>\",\n \"transaction\": \"<string>\",\n \"type\": \"custom\",\n \"fee_config\": {\n \"payer_address\": \"<string>\",\n \"self_managed_fees\": true\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-grid-environment", "<x-grid-environment>")
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/{address}/proposals")
.header("x-grid-environment", "<x-grid-environment>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"signer\": \"<string>\",\n \"transaction\": \"<string>\",\n \"type\": \"custom\",\n \"fee_config\": {\n \"payer_address\": \"<string>\",\n \"self_managed_fees\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-grid-environment"] = '<x-grid-environment>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"signer\": \"<string>\",\n \"transaction\": \"<string>\",\n \"type\": \"custom\",\n \"fee_config\": {\n \"payer_address\": \"<string>\",\n \"self_managed_fees\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"proposalAddress": "<string>",
"signer": "<string>",
"transactions": [
"<string>"
]
},
"metadata": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}Proposals
Create Proposal
Create a new proposal for multi-signature approval
POST
/
api
/
grid
/
v1
/
accounts
/
{address}
/
proposals
cURL
curl --request POST \
--url https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'x-grid-environment: <x-grid-environment>' \
--data '
{
"signer": "<string>",
"transaction": "<string>",
"type": "custom",
"fee_config": {
"payer_address": "<string>",
"self_managed_fees": true
}
}
'import requests
url = "https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals"
payload = {
"signer": "<string>",
"transaction": "<string>",
"type": "custom",
"fee_config": {
"payer_address": "<string>",
"self_managed_fees": True
}
}
headers = {
"x-grid-environment": "<x-grid-environment>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-grid-environment': '<x-grid-environment>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
signer: '<string>',
transaction: '<string>',
type: 'custom',
fee_config: {payer_address: '<string>', self_managed_fees: true}
})
};
fetch('https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals', 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/{address}/proposals",
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([
'signer' => '<string>',
'transaction' => '<string>',
'type' => 'custom',
'fee_config' => [
'payer_address' => '<string>',
'self_managed_fees' => true
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"x-grid-environment: <x-grid-environment>"
],
]);
$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/{address}/proposals"
payload := strings.NewReader("{\n \"signer\": \"<string>\",\n \"transaction\": \"<string>\",\n \"type\": \"custom\",\n \"fee_config\": {\n \"payer_address\": \"<string>\",\n \"self_managed_fees\": true\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-grid-environment", "<x-grid-environment>")
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/{address}/proposals")
.header("x-grid-environment", "<x-grid-environment>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"signer\": \"<string>\",\n \"transaction\": \"<string>\",\n \"type\": \"custom\",\n \"fee_config\": {\n \"payer_address\": \"<string>\",\n \"self_managed_fees\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://grid.squads.xyz/api/grid/v1/accounts/{address}/proposals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-grid-environment"] = '<x-grid-environment>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"signer\": \"<string>\",\n \"transaction\": \"<string>\",\n \"type\": \"custom\",\n \"fee_config\": {\n \"payer_address\": \"<string>\",\n \"self_managed_fees\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"proposalAddress": "<string>",
"signer": "<string>",
"transactions": [
"<string>"
]
},
"metadata": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}Creates a proposal that requires consensus from multiple signers before execution. Proposals enable coordinated multi-party approval for transactions and account settings changes.
Available actions:
Supported tokens:
The proposal is now
Proposals are an enterprise-tier feature. Free and Pro tier accounts will receive a 403
Forbidden error.
Proposal Types
Custom Proposals
Execute arbitrary Solana transactions through your smart account:const response = await grid.createProposal(accountAddress, {
type: "custom",
transaction: serializedVersionedTransaction,
signer: creatorPublicKey,
});
Settings Proposals
Modify account configuration with up to 10 actions per proposal:const response = await grid.createProposal(accountAddress, {
type: "settings",
actions: [
{ type: "AddSigner", newSigner: { address: newKey, mask: 7 } },
{ type: "ChangeThreshold", newThreshold: 2 },
],
signer: creatorPublicKey,
});
AddSigner, RemoveSigner, ChangeThreshold, SetTimeLock, AddSpendingLimit, RemoveSpendingLimit, SetArchivalAuthority
Transfer Proposals
Transfer tokens or SOL from your smart account through a proposal:const response = await grid.createProposal(accountAddress, {
type: "transfer",
token: "usdc",
destination: recipientPublicKey,
rawAmount: 1000000,
signer: creatorPublicKey,
});
sol, usdc, usdt, pyusd, eurc, or a custom mint
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | object | Yes | Standard token name or custom mint object |
destination | string | Yes | Recipient’s Solana address |
rawAmount | number | Yes | Amount in the token’s smallest unit (e.g. lamports for SOL, 10^6 for USDC) |
mintDecimals | number | No | Override decimal precision; validated against on-chain mint data |
signer | string | Yes | Public key of the proposal creator |
Permission Requirements
The signer must haveCAN_INITIATE permission (mask includes value 1). The signer address cannot be the smart account address itself.
Transaction Splitting
Solana transactions have a 1232-byte limit. If your proposal exceeds this, the API returns multiple transactions that must be signed and submitted in order.Fee Configuration
By default, the Grid paymaster sponsors fees. Enterprise accounts can specify custom fee handling:{
fee_config: {
currency: "SOL",
payer_address: payerPublicKey,
self_managed_fees: true // Skip simulation
}
}
Implementation Flow
1
Prepare Proposal
Build your transaction or define settings actions.
2
Create Proposal
Call this endpoint. Returns unsigned transaction(s) and the proposal address.
3
Sign and Submit
Sign all returned transactions and submit to Solana in order.
Active and awaiting votes.
Related Endpoints
Authorizations
Your Grid API key from the Grid Dashboard
Headers
Target Solana environment
Path Parameters
Smart account address (Solana public key)
Body
application/json
Was this page helpful?