How it works
- The first authenticated call triggers a
loginmutation: email + password in, JWT out. - The JWT is cached on the
Usersingleton — shared acrossSTX,AsyncSTX, andSTXWebSocketin the same process. - Every subsequent call attaches
Authorization: Bearer <jwt>. - JWTs are valid for 60 minutes. The SDK preemptively refreshes at minute 59 via the
newTokenmutation. You do not have to handle expiry.
Where credentials come from
The SDK resolves credentials with this precedence (highest wins):Keyword arguments
Two-factor authentication
If the account has 2FA enabled, the first call raisesSTXTwoFactorRequiredException. Catch it, collect the code that arrived via email, and call confirm2Fa:
session_id is cached on the User singleton alongside the JWT, so subsequent calls in the same process don’t re-prompt.
For headless bots, use a service account without 2FA enabled. The 2FA flow is interactive by design.
Token lifetime
| Value | |
|---|---|
| Server-declared JWT lifetime | 60 minutes |
| SDK-side refresh trigger | 59 minutes |
| Refresh mutation | newToken |
| Shared across clients? | Yes — same process, same singleton |
Sharing auth across clients
TheUser singleton means one login covers all three clients:
Troubleshooting
STXAuthenticationFailedException: 'Invalid email or password'
STXAuthenticationFailedException: 'Invalid email or password'
The server rejected your credentials. Check:
- Is
STX_EMAIL/STX_PASSWORDpointing at the rightenv? (staging vs production accounts are distinct.) - Did you recently rotate the password?
- Try logging in through the web app with the same credentials.
STXTwoFactorRequiredException on every call
STXTwoFactorRequiredException on every call
The 2FA
session_id isn’t being cached between script invocations. The singleton is process-scoped — if you’re running a fresh Python process each time, 2FA will prompt every time. Use a service account without 2FA for CI/bots.'missing credentials' but my env vars are set
'missing credentials' but my env vars are set
Make sure the env vars are named exactly
STX_EMAIL and STX_PASSWORD (not STX_USERNAME or similar). Check with echo $STX_EMAIL in the same shell you’re running Python from.STXTokenExpiredException in a long-running service
STXTokenExpiredException in a long-running service
This should be rare — the SDK refreshes at 59 minutes automatically. If you see it, it usually means the
newToken refresh itself failed (for example, the account was disabled, or the server was momentarily unreachable). The next call will attempt a fresh login.Security best practices
- Never commit credentials to git. Use env vars or a profile file in
~/.stx/, both.gitignore’d by convention. - Rotate on compromise. Log in to the web app and change the password — the SDK will re-authenticate on the next call.
- Use a dedicated account for bots. Don’t share your personal login with automation.
- Set file permissions on
~/.stx/credentials. Chmod to600: - Prefer staging during development.
env="production"touches real balances.
Next
Configuration
Regions, environments, and profile files in detail.
Errors & retries
The full exception hierarchy and retry policy.

