curl --request POST \
--url https://api.sandbox.brickken.com/prepare-transactions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"method": "newInvest",
"chainId": "aa36a7",
"investorAddress": "0x1111111111111111111111111111111111111111",
"investorEmail": "investor@example.com",
"tokenSymbol": "EXMPL",
"investmentAmount": "1000"
}
'import requests
url = "https://api.sandbox.brickken.com/prepare-transactions"
payload = {
"method": "newInvest",
"chainId": "aa36a7",
"investorAddress": "0x1111111111111111111111111111111111111111",
"investorEmail": "investor@example.com",
"tokenSymbol": "EXMPL",
"investmentAmount": "1000"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
method: 'newInvest',
chainId: 'aa36a7',
investorAddress: '0x1111111111111111111111111111111111111111',
investorEmail: 'investor@example.com',
tokenSymbol: 'EXMPL',
investmentAmount: '1000'
})
};
fetch('https://api.sandbox.brickken.com/prepare-transactions', 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://api.sandbox.brickken.com/prepare-transactions",
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([
'method' => 'newInvest',
'chainId' => 'aa36a7',
'investorAddress' => '0x1111111111111111111111111111111111111111',
'investorEmail' => 'investor@example.com',
'tokenSymbol' => 'EXMPL',
'investmentAmount' => '1000'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$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://api.sandbox.brickken.com/prepare-transactions"
payload := strings.NewReader("{\n \"method\": \"newInvest\",\n \"chainId\": \"aa36a7\",\n \"investorAddress\": \"0x1111111111111111111111111111111111111111\",\n \"investorEmail\": \"investor@example.com\",\n \"tokenSymbol\": \"EXMPL\",\n \"investmentAmount\": \"1000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
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://api.sandbox.brickken.com/prepare-transactions")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"method\": \"newInvest\",\n \"chainId\": \"aa36a7\",\n \"investorAddress\": \"0x1111111111111111111111111111111111111111\",\n \"investorEmail\": \"investor@example.com\",\n \"tokenSymbol\": \"EXMPL\",\n \"investmentAmount\": \"1000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sandbox.brickken.com/prepare-transactions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"method\": \"newInvest\",\n \"chainId\": \"aa36a7\",\n \"investorAddress\": \"0x1111111111111111111111111111111111111111\",\n \"investorEmail\": \"investor@example.com\",\n \"tokenSymbol\": \"EXMPL\",\n \"investmentAmount\": \"1000\"\n}"
response = http.request(request)
puts response.read_body{
"transactions": [
{
"from": "0x1234567890abcdef1234567890abcdef12345678",
"to": "0xabcdef1234567890abcdef1234567890abcdef12",
"value": "0x00",
"nonce": 3792,
"chainId": 11155111,
"data": "0xd362e8a70000000000000000000000000000000000000000000000000000000000000040...",
"type": 2,
"maxPriorityFeePerGas": "1150000",
"maxFeePerGas": "1156037",
"gasLimit": "0xccef"
}
],
"txId": "0x46adea7bdf49c576a760102e0d6bc9ecd650b3998588cd3d7f576a7973426aad",
"info": {
"tokenizerEmail": "tokenizer@example.com",
"tokenSymbol": "EXMPL",
"investorEmail": "investor@example.com"
}
}newInvest
Invest in an open Security Token Offering on behalf of a registered investor.
curl --request POST \
--url https://api.sandbox.brickken.com/prepare-transactions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"method": "newInvest",
"chainId": "aa36a7",
"investorAddress": "0x1111111111111111111111111111111111111111",
"investorEmail": "investor@example.com",
"tokenSymbol": "EXMPL",
"investmentAmount": "1000"
}
'import requests
url = "https://api.sandbox.brickken.com/prepare-transactions"
payload = {
"method": "newInvest",
"chainId": "aa36a7",
"investorAddress": "0x1111111111111111111111111111111111111111",
"investorEmail": "investor@example.com",
"tokenSymbol": "EXMPL",
"investmentAmount": "1000"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
method: 'newInvest',
chainId: 'aa36a7',
investorAddress: '0x1111111111111111111111111111111111111111',
investorEmail: 'investor@example.com',
tokenSymbol: 'EXMPL',
investmentAmount: '1000'
})
};
fetch('https://api.sandbox.brickken.com/prepare-transactions', 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://api.sandbox.brickken.com/prepare-transactions",
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([
'method' => 'newInvest',
'chainId' => 'aa36a7',
'investorAddress' => '0x1111111111111111111111111111111111111111',
'investorEmail' => 'investor@example.com',
'tokenSymbol' => 'EXMPL',
'investmentAmount' => '1000'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$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://api.sandbox.brickken.com/prepare-transactions"
payload := strings.NewReader("{\n \"method\": \"newInvest\",\n \"chainId\": \"aa36a7\",\n \"investorAddress\": \"0x1111111111111111111111111111111111111111\",\n \"investorEmail\": \"investor@example.com\",\n \"tokenSymbol\": \"EXMPL\",\n \"investmentAmount\": \"1000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
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://api.sandbox.brickken.com/prepare-transactions")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"method\": \"newInvest\",\n \"chainId\": \"aa36a7\",\n \"investorAddress\": \"0x1111111111111111111111111111111111111111\",\n \"investorEmail\": \"investor@example.com\",\n \"tokenSymbol\": \"EXMPL\",\n \"investmentAmount\": \"1000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sandbox.brickken.com/prepare-transactions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"method\": \"newInvest\",\n \"chainId\": \"aa36a7\",\n \"investorAddress\": \"0x1111111111111111111111111111111111111111\",\n \"investorEmail\": \"investor@example.com\",\n \"tokenSymbol\": \"EXMPL\",\n \"investmentAmount\": \"1000\"\n}"
response = http.request(request)
puts response.read_body{
"transactions": [
{
"from": "0x1234567890abcdef1234567890abcdef12345678",
"to": "0xabcdef1234567890abcdef1234567890abcdef12",
"value": "0x00",
"nonce": 3792,
"chainId": 11155111,
"data": "0xd362e8a70000000000000000000000000000000000000000000000000000000000000040...",
"type": 2,
"maxPriorityFeePerGas": "1150000",
"maxFeePerGas": "1156037",
"gasLimit": "0xccef"
}
],
"txId": "0x46adea7bdf49c576a760102e0d6bc9ecd650b3998588cd3d7f576a7973426aad",
"info": {
"tokenizerEmail": "tokenizer@example.com",
"tokenSymbol": "EXMPL",
"investorEmail": "investor@example.com"
}
}POST /prepare-transactions with method=newInvest.
The prepared transaction is signed and sent by investorAddress, not by the tokenizer.
Prerequisites
newInvest prepares a single buyToken transaction and nothing else. It does not whitelist the
investor and it does not return an approve transaction. Everything below must already be true
before you call it.
The investor exists, with a wallet address
whitelist or
create-kyc-link, passing the wallet address. Verify
with GET /get-investor-info: a null walletAddress fails the call.The investor’s email must be different from the tokenizer account’s email, or no investor record
is created and lookups return Investor not found.The investor's wallet is whitelisted for this token
GET /get-whitelist-status?tokenSymbol=…&address=…. Whitelisting is per token, and
the whitelist transaction must be signed, sent, and confirmed — preparing it is not enough.KYC is approved, or not required
GET /get-investor-info → complianceStatus must be APPROVED or NOT_REQUIRED. In Sandbox,
create the investor with needKyc: false and it reads NOT_REQUIRED from the start.The investor approved this offering's escrow
approve with the offering’s escrowAddress as
spenderAddress. Allowances are per spender: approving one offering’s escrow grants nothing to
another’s.The investor holds enough payment token
The offering is open
startDate must have passed and endDate must not have. Check with GET /get-sto-by-id.minInvestment and maxInvestment.
needKyc: false and skip identity verification entirely. The KYC flow
is a Sumsub verification with document upload, not something you need to prove your integration
works, and every test investor you create without it is one less manual step in your loop.Rehearse the real KYC flow once before you go live, since needKyc: false is rejected in
production with needKyc=false is only available in the sandbox environment.How failures surface
The whitelist, the allowance, and the balance are enforced by the contract, not by an API precondition check — they are caught when the API simulates the transaction. So they come back as contract reverts, not as friendly messages:| Revert | Meaning |
|---|---|
0x3ecda1e8 IssuanceNotStarted(address) | The offering has not opened yet. Check the Z suffix on the startDate you used at newSto. |
0xaafefe9b UserIsNotWhitelisted(address) | The wallet in the argument is not whitelisted for this token. |
Panic 0x11 | Arithmetic underflow — a zero or insufficient allowance to this escrow, or not enough payment token in the wallet. |
400 naming the compliance problem. A revert therefore always means chain state — a
timestamp, a whitelist entry, an allowance, or a balance — never a compliance verdict.Next step
Preparing does not touch the chain. The response gives youtxId and an array of unsigned transactions — you still have to sign and submit them.
Sign every returned transaction
signerAddress, or investorAddress for newInvest and claimTokens. It must be whitelisted by Brickken, and it needs native gas on the target chain.Submit the signed payloads
POST them to /send-transactions as { txId, signedTransactions } and Brickken broadcasts for you.If you would rather broadcast yourself, prepare with executionMode: "client-broadcast" and confirm afterwards with { txId, txHash } instead.Poll until it confirms
GET /get-transaction-status with the txId. A pending status means it is broadcast but not yet mined — do not resubmit.Authorizations
Body
Required. Operation to prepare. Must be newInvest for this endpoint.
newInvest "newInvest"
Required. Blockchain network identifier. Hex format is recommended, for example Sepolia aa36a7.
"aa36a7"
Required. Investor wallet address. It is used as the signer and sender of the prepared transaction, and must already be whitelisted for the token and hold both the payment-token balance and an allowance towards the offering's escrow.
^0x[a-fA-F0-9]{40}$"0x1111111111111111111111111111111111111111"
Required. Email of the investor. The investor must already exist in Brickken with a wallet address on record, and must be KYC eligible: complianceStatus from GET /get-investor-info has to be APPROVED or NOT_REQUIRED. The email must differ from the tokenizer account's email, otherwise no investor record exists and the call returns Investor not found.
"investor@example.com"
Required. Symbol of the token being offered.
"EXMPL"
Required. Payment-token amount to invest. It must fall within the offering's minimum and maximum investment. An amount above the wallet's balance, or above its allowance towards the escrow, reverts with Solidity panic 0x11.
"1000"
Optional and ignored for this method. The prepared transactions are always signed and sent by investorAddress.
"0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
Optional. Symbol of the payment token used for the investment. If omitted, the chain default payment token is used.
"USDT"
Response
Successful response
Array of unsigned transaction objects ready for signing
Show child attributes
Show child attributes
Unique identifier for this transaction batch (required for /send-transactions). This is NOT a blockchain transaction hash.
"0x46adea7bdf49c576a760102e0d6bc9ecd650b3998588cd3d7f576a7973426aad"
Metadata about the operation
Show child attributes
Show child attributes