What you can do
Describes @stxapp/stx-typescript 0.9.0, the current release.
One SDK works against every STX exchange. Not every exchange has every feature yet: OAuth apps are on US demo first, for example. A call that needs a feature the exchange does not have throws STXNotAvailableException, and capabilities() tells you up front, so the same code runs against Ontario production today and picks up new features as each exchange gets them.
Features and their calls
Section titled “Features and their calls”| Feature | Id | SDK calls | Guide |
|---|---|---|---|
| Markets, events and prices | markets(), iterMarkets(), market(), events(), iterEvents(); channels ws.ticker(), ws.market(), ws.orderbook(), ws.trades(), ws.marketStats(), ws.marketUpdates() |
Markets, WebSockets | |
| Trading | placeOrder(), placeOrders(), cancelOrder(), cancelOrders(), cancelAllOrders(), order(), orders() |
Trading | |
| Your account | me(), balance(), positions(), fills(), settlements(), deposits(), withdrawals(), fees(), accountMarketStats(); channels ws.accountView(), ws.balances(), ws.orders(), ws.fills(), ws.positions(), ws.settlements() |
Portfolio | |
| Event players | event_players |
eventPlayers(), statsFor() |
Markets |
| Leaderboard and public profile | leaderboard |
leaderboard(), leaderboardMe(), updateProfile() |
Leaderboard |
| Combos | combos |
ws.comboPlayer(), ws.comboMaker() |
Combos |
| OAuth | oauth |
discoverMetadata(), OAuthClient.discover() |
Build an app |
| Members connect their STX account | oauth.member |
oauth.beginAuthorization(), oauth.completeAuthorization(), oauth.memberClient(), oauth.refreshToken(), oauth.unlink(), oauth.revoke() |
Sign in with STX |
| App tokens | oauth.app |
oauth.appClient(), oauth.appToken(), oauth.clientCredentials() |
Acting as your app |
| Sign in with STX | connect |
createConnect(): start(), finish(), refresh(), disconnect(), userInfo() |
Sign in with STX |
| Terms accepted on an STX page | terms.stx_page |
STXTermsRequiredException.termsUrl, termsUrlWithReturn(), the onTermsRequired session hook |
When STX has new terms |
| Creating markets | markets.create |
createMarkets() (with dryRun), as an app holding markets.write |
Creating markets |
| Market types | market_types |
marketTypes() |
Creating markets |
Features without an id are on every exchange. Members still being verified (STXAccountPendingException) and IP allow-list refusals (STXIPNotAllowedException) come with OAuth wherever it is on.
Where each feature is available
Section titled “Where each feature is available”Checked against the live exchanges on 2026-10-21, and Ontario demo again on 2026-10-23.
| Feature | US demo (demo.stxapp.io) |
Ontario demo (demo.stxapp.ca) |
Ontario production (api.on.stxapp.ca) |
|---|---|---|---|
| Markets, trading, your account | yes | yes | yes |
event_players |
yes | yes | yes |
leaderboard |
yes | no | no |
oauth |
yes | no | no |
oauth.member |
yes | no | no |
oauth.app |
yes | no | no |
connect |
yes | no | no |
terms.stx_page |
yes | no | no |
markets.create |
yes | no | no |
market_types |
yes | no | no |
Exchange version (/api_version) |
3.3.196 |
3.3.194-5 |
3.3.186-6 |
Exchanges gain features with their releases, so ask the exchange rather than this table when your code needs to know.
Ask the exchange: capabilities()
Section titled “Ask the exchange: capabilities()”import { STX } from "@stxapp/stx-typescript";
const stx = new STX({ environment: "ontario-demo" });const caps = await stx.capabilities();console.log(caps.toJSON());On Ontario demo:
{ "host": "demo.stxapp.ca", "apiVersion": "3.3.194-5", "checkedAt": "2026-10-23T17:37:03.512Z", "scopesSupported": [], "features": { "oauth": false, "oauth.member": false, "oauth.app": false, "connect": false, "terms.stx_page": false, "markets.create": false, "market_types": false, "event_players": true, "leaderboard": false, "combos": true }}On US demo every feature is true, and scopesSupported is the list the exchange publishes:
[ "openid", "profile.read", "balance.read", "portfolio.read", "orders.read", "transfers.read", "orders.write", "market_data", "events", "markets.write"]caps.features.combosistrue,false, ornullwhen it could not be checked (a check that needs credentials on a client without them, or an exchange that did not answer).caps.has("combos")is true only when the exchange is known to have it.caps.require("combos")throwsSTXNotAvailableExceptionwhen the exchange is known not to have it.
The answer is cached on the client; capabilities({ refresh: true }) checks again. A missing feature never makes capabilities() or new STX() throw, and the calls you already make are unaffected: only a call that needs the missing feature throws.
How each feature is decided
Section titled “How each feature is decided”Every check reads what the exchange publishes. Nothing is created, changed or cancelled.
| Id | Available when |
|---|---|
oauth |
GET /.well-known/oauth-authorization-server returns the discovery document. A 404, or a 403 page from in front of the exchange (not JSON), means no. |
oauth.member |
OAuth is on, grant_types_supported lists authorization_code, and scopes_supported lists a member scope such as orders.read. |
oauth.app |
OAuth is on and grant_types_supported lists client_credentials. |
connect |
OAuth is on and scopes_supported lists openid. |
terms.stx_page |
Members can connect and scopes_supported does not list terms.write. |
markets.create |
App tokens are on and scopes_supported lists markets.write. |
market_types |
A signed GET /api/v1/types answers. A 404 with {"error": "not_found"} (no such route) means no. |
event_players |
A signed GET /api/v1/events/{event_id}/players for an event that does not exist answers 404 {"error": "event not found"}. A 404 {"error": "not_found"} means no such route. |
leaderboard |
A signed GET /api/v1/leaderboard?limit=1 answers. A 404 (no such route, or {"error": "Leaderboard is not enabled"}) means no. |
combos |
A join of the combo player topic for a user id nobody has is refused as unauthorized. unmatched topic means the exchange has no combo topics. |
The OAuth discovery document and its fields are described in OAuth, and the combo topics in Combo negotiation.
When a feature is not available
Section titled “When a feature is not available”STXNotAvailableException extends STXAPIException, so it is an STXException like every other SDK error. It carries:
feature: the id from the tables above, orscope:<name>for a scope the exchange does not offer.host: the exchange that answered.message: a sentence naming both.statusCodeandbody: the answer it was decided from, when there was one.
Captured on Ontario demo on 2026-10-21:
await stx.marketTypes();// STXNotAvailableException: Market types are not available on demo.stxapp.ca yet.// feature: "market_types", host: "demo.stxapp.ca", statusCode: 404, body: { error: "not_found" }
await discoverMetadata("https://demo.stxapp.ca");// STXNotAvailableException: OAuth is not available on demo.stxapp.ca yet.// feature: "oauth", host: "demo.stxapp.ca", statusCode: 404STXCombosUnavailableException is still the channel exception it was, and instanceof STXNotAvailableException is true for it too, so either catch works:
import { STXNotAvailableException } from "@stxapp/stx-typescript";
try { const player = await ws.comboPlayer(); // ...} catch (err) { if (err instanceof STXNotAvailableException) { console.log(`${err.feature} is not on ${err.host} yet; hiding combos`); } else { throw err; }}Which calls throw it, by feature:
| Id | Throws from |
|---|---|
combos |
ws.comboPlayer(), ws.comboMaker() |
oauth |
discoverMetadata(), OAuthClient.discover(), and any token, refresh, revoke or introspect call on an exchange without OAuth |
connect |
createConnect() calls, when the exchange has no OpenID configuration or userinfo endpoint |
market_types |
marketTypes() |
event_players |
eventPlayers(), statsFor() |
leaderboard |
leaderboard(), leaderboardMe(), updateProfile() |
markets.create |
createMarkets() |
scope:<name> |
clientCredentials(), appClient() calls and completeAuthorization(), when the exchange answers invalid_scope for a scope its discovery document does not list (the client must come from OAuthClient.discover() or fromMetadata() to know the list) |

