Authentication
The Go SDK supports manual web-based login (request token → access token) and fully automated login with TOTP.
Prerequisites
Before authenticating, ensure you have:
- Valid Arrow user credentials
- Registered redirect URL in the Developer Apps section
- Your
appIDandappSecretfrom the Trading API section - Static IP registered (mandatory per SEBI Circular)
Authentication Methods
Method 1: Web-based Login (Manual)
Redirect users to Arrow's login page, then exchange the callback request-token.
package main
import (
"fmt"
"log"
"github.com/arrow-trade/go-arrow/arrow"
)
func main() {
client := arrow.NewClient("YOUR_APP_ID", "YOUR_APP_SECRET")
// Prints https://app.arrow.trade/app/login?appId=<appID> and reads the token from stdin
client.Login()
fmt.Println("Access token:", client.GetToken())
}
If you already have the request token (for example from your OAuth callback), skip the stdin prompt:
token, err := client.Authenticate("request_token_from_callback")
if err != nil {
log.Fatal(err)
}
fmt.Println("Access token:", token)
Authenticate SHA256-hashes appId:appSecret:request-token and POSTs to /auth/app/authenticate-token on https://edge.arrow.trade. It sends both checkSum and checksum in the JSON body.
Login URL query key
Login() prints ?appId= (lowercase d). Build the same URL yourself with fmt.Sprintf("https://app.arrow.trade/app/login?appId=%s", appID).
Callback URL
After successful login, Arrow redirects to your registered URL with:
request-token: Temporary authentication tokenchecksum: SHA256 hash for verification
Method 2: Automated Login (TOTP)
AutoLogin runs the full credential + TOTP + token-exchange flow. The app secret is taken from NewClient, not passed again.
| Parameter | Required | Description |
|---|---|---|
username |
✓ | Arrow user ID |
password |
✓ | Account password |
totpSecret |
✓ | Base32 TOTP secret |
if err := client.AutoLogin("YOUR_USER_ID", "YOUR_PASSWORD", "YOUR_TOTP_SECRET"); err != nil {
log.Fatal(err)
}
fmt.Println("Logged in. Token:", client.GetToken())
Flow:
- POST
https://api.arrow.trade/auth/app/login - Generate a 6-digit TOTP (30s period, SHA1)
- POST
https://edge.arrow.trade/auth/validate-2fa - Parse
request-tokenfrom the redirect URL Authenticate(requestToken)— storesTokenon the client
TOTP Secret
The totpSecret is the base32 encoded secret used to generate time-based one-time passwords. You can go to the profile page and copy the TOTP secret if enabled. If not enabled, log out of the current session and click on forgot password and complete the flow.
Session Management
Get Current Token
Set Token Manually
Reuse a valid token from a previous session (still within 24 hours):
Clear Session
There is no InvalidateSession helper. Clear the token:
Token Lifecycle
| Aspect | Details |
|---|---|
| Validity | 24 hours from generation |
| After expiry | New login required — refresh tokens are not supported |
| Storage | Store securely; never expose in client-side code |
Token Expiration
Access tokens expire after 24 hours due to regulatory compliance. Implement a re-login path in long-running processes.
Authentication Response
Authenticate returns the access token string and updates client.Config. The wire payload looks like:
{
"status": "success",
"data": {
"name": "ABHISHEK JAIN",
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"userId": "AJ0001"
}
}
| Field | Type | Description |
|---|---|---|
name |
string | User's full name |
token |
string | JWT access token |
userId |
string | Unique user identifier |
User Information
After authentication, retrieve profile and limits:
user, err := client.GetUserDetails()
if err != nil {
log.Fatal(err)
}
fmt.Println("Logged in as:", user.Data.Name)
fmt.Println("Exchanges:", user.Data.Exchanges)
limits, err := client.GetLimits()
if err != nil {
log.Fatal(err)
}
fmt.Println("Usable margin:", limits.Data.Margin["usableMargin"])
Wrapped user and limits
GetUserDetails returns *User with Data + Status (use user.Data.Name). GetLimits is the same (limits.Data.Margin). Positions, holdings, and order book return unwrapped slices.
Error Handling
if err := client.AutoLogin(userID, password, totpSecret); err != nil {
log.Printf("Authentication failed: %v", err)
return
}
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Invalid checksum | Incorrect SHA256 generation | Verify appId:appSecret:request-token format |
| Token expired | Request token timeout | Restart authentication flow |
| Invalid credentials | Wrong user ID or password | Verify credentials |
| Invalid TOTP | Incorrect or expired OTP | Check TOTP secret and system time sync |
Security Best Practices
Security Notice
- Never expose
appSecretin client-side code - Never commit credentials to version control
- Always use environment variables for sensitive data
- Always use HTTPS for all API communications
Environment Variables Example
package main
import (
"log"
"os"
"github.com/arrow-trade/go-arrow/arrow"
)
func main() {
client := arrow.NewClient(os.Getenv("ARROW_APP_ID"), os.Getenv("ARROW_APP_SECRET"))
if err := client.AutoLogin(
os.Getenv("ARROW_USER_ID"),
os.Getenv("ARROW_PASSWORD"),
os.Getenv("ARROW_TOTP_SECRET"),
); err != nil {
log.Fatal(err)
}
}
Complete Example
package main
import (
"fmt"
"log"
"os"
"github.com/arrow-trade/go-arrow/arrow"
)
func initializeClient() (*arrow.Client, error) {
client := arrow.NewClient(os.Getenv("ARROW_APP_ID"), os.Getenv("ARROW_APP_SECRET"))
if err := client.AutoLogin(
os.Getenv("ARROW_USER_ID"),
os.Getenv("ARROW_PASSWORD"),
os.Getenv("ARROW_TOTP_SECRET"),
); err != nil {
return nil, fmt.Errorf("login: %w", err)
}
user, err := client.GetUserDetails()
if err != nil {
return nil, fmt.Errorf("user details: %w", err)
}
fmt.Println("Logged in as:", user.Data.Name)
return client, nil
}
func main() {
client, err := initializeClient()
if err != nil {
log.Fatal(err)
}
_ = client
}