Quickstart
This walks you from a new account to a working integration in Test: a key, a vault for one customer, a deposit into it and a payment out of it. Everything happens on Sepolia, Ethereum’s test network, where coin is free and worth nothing, so nothing here can cost you money.
You need Go 1.22 or later, or Node 20 or later, on a server or your own machine, and a crypto wallet that can send Sepolia ETH (any Ethereum wallet can, once switched to Sepolia).
-
Create a Test account.
Sign up at app.sotirea.com/sign-up. Your new organisation is a Test organisation: its networks are test networks, and Live opens only after you apply and Sotirea approves it. Everyone sets up two-factor sign-in on their first visit.
-
Create an API key.
In the console, open Developers, then API keys, and choose Create a key. Name it after what will use it, such as Quickstart. Under What it can do, choose Integration: it can open vaults, request payments, manage webhooks and read balances, but never approve a payment or manage people.
Because Integration can move money, a second person who manages roles has to confirm it. If you are the only person in your organisation, nobody else could, so it takes effect at once.
The key is shown once. Keep it on your server, in an environment variable or a secrets manager:
Terminal window export SOTIREA_API=https://api.sotirea.comexport SOTIREA_API_KEY=sot_test_EXAMPLEkeyEXAMPLEkeyEXAMPLEkeyEXAMPLEA Test key starts
sot_test_. Authentication has the rest. -
Install a client.
Terminal window go get sotirea.com/sdk/go # Gonpm install @sotirea/sdk # TypeScript -
Check the key.
client := sotirea.New(os.Getenv("SOTIREA_API"),os.Getenv("SOTIREA_API_KEY"),)me, _, err := client.GetMe(ctx)if err != nil {log.Fatal(err)}org := me.Organisationfmt.Printf("%s, in %s (%s)\n", me.Key.Name, org.Name, org.Environment)const sotirea = new Sotirea({baseUrl: process.env.SOTIREA_API ?? 'https://api.sotirea.com',apiKey: process.env.SOTIREA_API_KEY ?? '',})const me = await sotirea.getMe()const org = me.organisationconsole.log(`${me.key.name}, in ${org.name} (${org.environment})`)It prints the key’s name, your organisation and
test. If it saysunauthenticatedinstead, the key was copied incompletely or has been revoked. -
Open a vault for a customer.
Give each customer their own vault, named by your own id for them, here
cust_1001. Asking again with the same reference answers the same vault rather than opening another, so you can call this whenever a customer needs their address without remembering whether you already did.vault, _, err := client.CreateVault(ctx, sotirea.VaultRequest{Reference: "cust_1001",})if err != nil {log.Fatal(err)}for _, a := range vault.Addresses {if a.ChainID == sepolia {fmt.Printf("Pay vault %d at %s\n", vault.ID, a.Address)}}const vault = await sotirea.createVault({ reference: 'cust_1001' })for (const a of vault.addresses) {if (a.chain_id === sepolia) {console.log(`Pay vault ${vault.id} at ${a.address}`)}}The vault lists an address for every network your organisation can use, and it is the same address on each. Nothing is written to any network yet: the vault is created on a network the first time it pays out there, and it can receive before that.
-
Receive a test deposit.
From your wallet, send a little Sepolia ETH to the vault’s address. Then list what has arrived:
deposits, err := client.ListDeposits(ctx, sotirea.ListDepositsParams{Vault: vault.ID,})if err != nil {log.Fatal(err)}for _, d := range deposits.Items {// arriving: seen on the network, not yet final.// received: final, and in the vault's balance.fmt.Println(d.State, d.Amount, d.Symbol, "from", d.From)}const deposits = await sotirea.listDeposits({ vault: vault.id })for (const d of deposits.items) {// arriving: seen on the network, not yet final.// received: final, and in the vault's balance.console.log(d.state, d.amount, d.symbol, 'from', d.from)}The deposit shows as
arrivingwithin a few minutes of being sent: Sotirea reads test networks every two minutes. On Sepolia it becomesreceivedabout 15 minutes later, once the network has made it final, and only then is it in the vault’s spendable balance. Webhooks can tell your server about each step instead of your code asking. -
Top up the network fees.
Every payment pays the network a fee, in the network’s own coin. Sotirea pays it from a pool your organisation tops up, one per network, never from the vault or the amount you send. In the console, open Network fees, choose Sepolia under Add coin and send some Sepolia ETH to the address shown. Network fees has the detail.
-
Send a payment.
First ask what the payment would come to: whether your payment rules allow it and whether the fee pool covers it. Then send it, with an Idempotency-Key that names it.
pay := sotirea.PaymentRequest{VaultID: vault.ID,ChainID: sepolia,To: os.Args[1],Amount: "1000000000000000", // 0.001 ETH: ETH has 18 decimalsReference: "payout-42",}quote, _, err := client.QuotePayment(ctx, pay)if err != nil {log.Fatal(err)}if quote.Outcome != "allowed" || !quote.GasPoolCovers {log.Fatalf("not sending: %s %s", quote.Outcome, quote.Reason)}// The Idempotency-Key, "payout-42", names this payment. If the// answer is lost, sending the same request again answers the same// payment: it never pays twice.payment, _, err := client.SendPayment(ctx, "payout-42", pay)if err != nil {log.Fatal(err)}fmt.Println(payment.ID, payment.Status)const pay = {vault_id: vault.id,chain_id: sepolia,to,amount: '1000000000000000', // 0.001 ETH: ETH has 18 decimalsreference: 'payout-42',}const quote = await sotirea.quotePayment(pay)if (quote.outcome !== 'allowed' || !quote.gas_pool_covers) {throw new Error(`not sending: ${quote.outcome} ${quote.reason ?? ''}`)}// The Idempotency-Key, 'payout-42', names this payment. If the// answer is lost, sending the same request again answers the same// payment: it never pays twice.const payment = await sotirea.sendPayment('payout-42', pay)console.log(payment.id, payment.status)The answer comes at once, with the payment
requested. It moves on tosigned,sent, andconfirmedonce final. Follow it with Get a payment using theidit answered, which isapi:followed by your organisation’s id and the Idempotency-Key.The vault’s first payment on a network also creates the vault there, so its fee is a little higher. The quote includes that.
When it works in Test
Section titled “When it works in Test”A Live key, starting sot_live_, runs the same code against real networks and real money,
once your organisation has been approved for Live. Apply from Go live in the console. The
network ids change, from Sepolia (eip155:11155111) to Ethereum (eip155:1) for example,
and nothing else in your code has to.