Quickstart
This quickstart runs a small Go program against the sandbox. It signs a staff member in, lists the organizations they belong to, and prints each one’s balances.
Before you start
Section titled “Before you start”You need:
- Go 1.27 or later.
curlandjqfor the curl equivalents below.- A sandbox user with an authenticator app set up. Ask your Ele contact for sandbox access.
- The user must be an owner or admin of at least one organization. Members cannot read balances.
- The example Go module, set up as shown in Example code. It holds a small shared HTTP helper and one directory per example.
Run it
Section titled “Run it”From the examples directory, set the user’s credentials and run the program.
read -rs asks for the password without echoing it or saving it in your shell
history. Use a fresh code from the authenticator app:
export ELE_EMAIL=you@example.comread -rs ELE_PASSWORD && export ELE_PASSWORDELE_TOTP_CODE=123456 go run ./quickstartThe program prints each organization’s name and ID, then one line per currency with the available balance:
Acme Ltd (0190f1c2-…) USD 1250.00 EUR 80.00What the program does
Section titled “What the program does”// Quickstart: sign in, list your organizations, print their balances.//// ELE_EMAIL=… ELE_PASSWORD=… ELE_TOTP_CODE=123456 go run ./quickstartpackage main
import ( "context" "errors" "fmt" "log" "net/url" "os"
"github.com/elephants-labs/ele/developers/examples/internal/ele")
// signInResult covers the sign-in and MFA responses. Status says which// fields are set.type signInResult struct { Status string `json:"status"` Token string `json:"token"` ChallengeToken string `json:"challenge_token"`}
func main() { ctx := context.Background() c := ele.FromEnv()
var signIn signInResult must(c.Do(ctx, "POST", "/api/v1/auth/sign-in", map[string]string{"email": os.Getenv("ELE_EMAIL"), "password": os.Getenv("ELE_PASSWORD")}, &signIn))
switch signIn.Status { case "mfa_required": must(c.Do(ctx, "POST", "/api/v1/auth/mfa", map[string]string{"challenge_token": signIn.ChallengeToken, "code": os.Getenv("ELE_TOTP_CODE")}, &signIn)) case "totp_setup_required": log.Fatal("this account must finish authenticator (TOTP) setup before it can sign in; see the sign-in guide") } if signIn.Status != "authenticated" { log.Fatalf("sign-in ended in status %q", signIn.Status) } c.Token = signIn.Token
type organization struct { ID string `json:"id"` Name string `json:"name"` } // The list is paginated: follow next_cursor until has_more is false. var all []organization path := "/api/v1/organizations" for { var page struct { Data []organization `json:"data"` HasMore bool `json:"has_more"` NextCursor string `json:"next_cursor"` } must(c.Do(ctx, "GET", path, nil, &page)) all = append(all, page.Data...) if !page.HasMore { break } path = "/api/v1/organizations?cursor=" + url.QueryEscape(page.NextCursor) }
for _, org := range all { var balance struct { Balances []struct { Currency string `json:"currency"` Available string `json:"available"` } `json:"balances"` } err := c.Do(ctx, "GET", "/api/v1/organizations/"+url.PathEscape(org.ID)+"/balance", nil, &balance) // Only owners and admins may read balances; skip the rest. var apiErr *ele.APIError if errors.As(err, &apiErr) && apiErr.Type == "forbidden" { fmt.Fprintf(os.Stderr, "skipping %s (%s): %v\n", org.Name, org.ID, err) continue } must(err) fmt.Printf("%s (%s)\n", org.Name, org.ID) for _, b := range balance.Balances { fmt.Printf(" %s %s\n", b.Currency, b.Available) } }}
func must(err error) { if err != nil { log.Fatal(err) }}The curl commands below do the same steps by hand. They assume ELE_API_BASE
is set, as shown in Environments.
1. Sign in with a password
Section titled “1. Sign in with a password”POST /api/v1/auth/sign-in checks the email and password. It does not return a
session yet if the account has an authenticator app. It returns
status: "mfa_required" and a challenge_token instead.
jq -n '{email: env.ELE_EMAIL, password: env.ELE_PASSWORD}' | curl -s "$ELE_API_BASE/api/v1/auth/sign-in" \ -H 'Content-Type: application/json' \ --data @-jq reads the credentials from the environment and encodes them as JSON, so a
password with quotes or backslashes still works. --data @- reads the body
from standard input, so the password does not appear in the process list.
{ "status": "mfa_required", "challenge_token": "…", "methods": ["totp"] }2. Send the authenticator code
Section titled “2. Send the authenticator code”POST /api/v1/auth/mfa takes the challenge token and the code. On success it
returns status: "authenticated" and a session token.
curl -s "$ELE_API_BASE/api/v1/auth/mfa" \ -H 'Content-Type: application/json' \ -d "{\"challenge_token\": \"$CHALLENGE_TOKEN\", \"code\": \"$ELE_TOTP_CODE\"}"Sign-in can end in other statuses. Authentication explains each one.
3. List organizations
Section titled “3. List organizations”Send the token as a bearer token. GET /api/v1/organizations returns the
organizations the user belongs to, one page at a time. The program follows
next_cursor until has_more is false.
curl -s "$ELE_API_BASE/api/v1/organizations" \ -H "Authorization: Bearer $ELE_TOKEN"{ "data": [{ "id": "…", "name": "Acme Ltd", "role": "admin", "permissions": ["…"] }], "has_more": false, "next_cursor": null }4. Read balances
Section titled “4. Read balances”GET /api/v1/organizations/{organization_id}/balance returns a balances
list with one entry per currency. Amounts are decimal strings, such as
"1250.00". Each entry has available, pending and posted amounts.
curl -s "$ELE_API_BASE/api/v1/organizations/$ORGANIZATION_ID/balance" \ -H "Authorization: Bearer $ELE_TOKEN"Only owners and admins may read balances. For an organization where the user
is a member, Ele answers 403 with the error type forbidden. The program
notes the skip on standard error and moves on to the next organization. To
avoid the call altogether, check that the organization’s permissions list
contains organization.view_balances before you ask for its balance.
Next steps
Section titled “Next steps”- Authentication: every sign-in outcome, and how long a session lasts.
- Step-up and approvals: what happens when a call needs the person to confirm it.
- Organizations and roles: what each role may do.