Authentication
The SDK supports two ways to authenticate, and you pick one when you register it.
| When to use it | |
|---|---|
| API key (recommended) | Programmatic access: bots, services, anything unattended |
| Email and password | Apps acting on behalf of a person signing in, including 2FA flows |
API keys are the better fit for automated callers: there is no login call, no token to expire, and no refresh cycle, so a restart or an API deployment cannot leave a client without a session.
API-key authentication
Section titled “API-key authentication”Create a key under Account → API Keys, which gives you a key ID and an Ed25519 private key. Register the SDK with both:
using STX.Sdk;using STX.Sdk.Auth;using STX.Sdk.Settings;
services.ConfigureSTXServices( STXEnvironment.OntarioProduction, STXApiKeyCredentials.FromPemFile(keyId, "~/.stx/ontario.pem"));FromPemFile reads the key from disk so it never has to pass through an environment variable, shell history, or a process listing. If you already hold the PEM in memory, the constructor takes it directly:
new STXApiKeyCredentials(keyId, pemContents);There is nothing else to call. Every request is signed automatically with three headers:
| Header | |
|---|---|
X-STX-ACCESS-KEY |
Your key ID |
X-STX-ACCESS-TIMESTAMP |
Unix time in milliseconds |
X-STX-ACCESS-SIGNATURE |
Base64 Ed25519 signature over the timestamp, method, and path |
Knowing who you are
Section titled “Knowing who you are”There is no login response under API-key auth, so the user id that WebSocket channels need has to be fetched once:
var identity = await serviceProvider.GetRequiredService<STXIdentityService>().GetMeAsync();
Console.WriteLine($"{identity.UserId} / account {identity.AccountId} / scope {identity.Scope}");Do this at startup, before subscribing to any user-scoped channel.
Scopes
Section titled “Scopes”A key is issued as either read_only or read_write. A read_only key can query markets, orders, trades, and settlements, but placing or cancelling an order needs read_write. STXIdentity.Scope reports which one you hold.
Bringing your own cryptography
Section titled “Bringing your own cryptography”STXApiKeyCredentials also accepts a signing delegate, for callers who keep private keys in an HSM or a KMS rather than on disk:
new STXApiKeyCredentials(keyId, message => myHsm.SignEd25519(message));Existing integrations: email and password
Section titled “Existing integrations: email and password”The path still exists and keeps working: STXLoginService.LoginAsync acquires a JWT, STXSessionBackgroundService refreshes it, and everything else in the SDK behaves the same once authenticated. Nothing about it changed in 1.6.0.
It is the weaker option for a programmatic caller. A token expires, so a restart or an API deployment can leave a client without a session, and the credentials have to sit in memory for the background service to re-login. An API key has none of those properties.
Moving to an API key
Section titled “Moving to an API key”- Create a key under Account → API Keys and choose a scope.
read_onlycovers market, order, trade and settlement queries; placing or cancelling orders needsread_write. - Swap the registration to the API-key overload shown above. Nothing else in your code changes.
- Replace the user id you read from the login response with one
GetMeAsync()call at startup, since there is no login response to read. - Drop the
keepSessionAlivehandling. There is no token to keep alive.
Accept terms & conditions
Section titled “Accept terms & conditions”Until the current terms are accepted, authenticated calls are rejected.
To show the terms and accept only after the user has agreed, use the two explicit calls and do not call CheckTermsAndConditionsAsync at all:
var tnc = serviceProvider.GetRequiredService<STXTermsAndConditionsService>();
// Read-only: fetches the current terms without accepting anything.var current = await tnc.GetTermsAndConditionsAsync();
// Display current.Version to the user. Only once they have agreed in your UI:await tnc.AcceptTermsAndConditionsAsync(current.Version);AcceptTermsAndConditionsAsync also takes no argument, in which case the server accepts the version currently in effect. Pass the version you actually displayed where you can, so what was accepted is what the user saw.
Logout
Section titled “Logout”There is no server-side logout call.
With an API key there is nothing to end: requests are signed individually and no session is held. To revoke access, delete the key under Account → API Keys. Signatures made with it stop being accepted immediately.
On the legacy email/password path the session lives in STXUserStorage, which is a singleton. Stop the host to end it, and stop any channels you started with StopAsync.
Errors
Section titled “Errors”| Exception | Cause |
|---|---|
STXRequestFailedException |
Signature rejected. Check the host clock: the server allows 30 seconds of skew |
STXWrongCredentialsException |
Bad email/password, on the legacy path |
STXSessionExpiredException |
Token and refresh token both expired, on the legacy path |
STXGeoComplyException |
Geo-compliance check failed; user is outside a permitted jurisdiction |
See Errors & retries for the full list and recovery patterns.

