Skip to main content

SDKs

Senticore maintains SDKs for TypeScript, Python, and Rust. The TypeScript SDK is published on npm; Python and Rust remain repository previews until their public registry releases are completed. Endpoint contracts can still change before public mainnet.

Two environments

Live private-beta API base URL: https://api.sentico-labs.xyz. Sandbox API base URL: https://api.sentico-sandbox.xyz.

The TypeScript SDK is public on npm as @sentico-labs/sdk. Point it at the sandbox first - same routes, same signing, mock USDC, no collateral at risk. See Sandbox (launching — see Status).

Packages​

The npm release 0.3.0, published September 12, includes signed DMS controls, order opt-in metadata (encodeBslSessionOrder({ dmsEpoch })), client.trading.issuePrivateWsToken, and public V2 oracle reads. Use client.trading.controlDms / controlBslDms for signed controls and client.public.getMarketOracleV2 / getOracleFeedV2 for the /api/v2/public/... oracle routes. getMarketOracle retains its V1 route; getMarketOracleV2(id) answers 404 for a market without a V2 oracle profile and does not silently fall back to V1. See DMS integration and Oracle contracts. Rust remains a repository distribution.

LanguagePackage nameRepository versionRegistry statusRepo path
TypeScript@sentico-labs/sdk0.4.0npm latest is 0.3.0 (published 2026-09-12)sdks/typescript
Pythonsenticore-sdk0.1.0rc2Repository previewsdks/python
Rustsenticore-sdk0.1.0-rc.2Repository previewsdks/rust

Live Beta Configuration​

export SENTICORE_PUBLIC_HTTP_URL="https://api.sentico-labs.xyz"
export SENTICORE_TRADING_HTTP_URL="https://api.sentico-labs.xyz"
export SENTICORE_PUBLIC_WS_URL="wss://api.sentico-labs.xyz/api/v1/ws/public"
export SENTICORE_PRIVATE_WS_URL="wss://api.sentico-labs.xyz/api/v1/ws/private/{account}"
export SENTICORE_ORDER_ENTRY_HTTP_URL="https://api.sentico-labs.xyz"
export SENTICORE_ORDER_ENTRY_BINARY_PATH="/api/order-entry/binary"
export SENTICORE_ORDER_ENTRY_API_KEY="..." # optional provisioned lane key
export SENTICORE_BEARER_TOKEN="..."
export SENTICORE_BSL_TCP_HOST="bsl.sentico-labs.xyz"
export SENTICORE_BSL_TCP_PORT="9001"
export SENTICORE_BSL_TCP_TLS_SNI="bsl.sentico-labs.xyz"
export SENTICORE_FIX_HOST="fix.sentico-labs.xyz"
export SENTICORE_FIX_PORT="9878"
export SENTICORE_FIX_TLS_SNI="fix.sentico-labs.xyz"

Sandbox Configuration​

The same variables, one host substituted. Every port is unchanged.

export SENTICORE_PUBLIC_HTTP_URL="https://api.sentico-sandbox.xyz"
export SENTICORE_TRADING_HTTP_URL="https://api.sentico-sandbox.xyz"
export SENTICORE_PUBLIC_WS_URL="wss://api.sentico-sandbox.xyz/api/v1/ws/public"
export SENTICORE_PRIVATE_WS_URL="wss://api.sentico-sandbox.xyz/api/v1/ws/private/{account}"
export SENTICORE_ORDER_ENTRY_HTTP_URL="https://api.sentico-sandbox.xyz"
export SENTICORE_ORDER_ENTRY_BINARY_PATH="/api/order-entry/binary"
export SENTICORE_BSL_TCP_HOST="bsl.sentico-sandbox.xyz"
export SENTICORE_BSL_TCP_PORT="9001"
export SENTICORE_BSL_TCP_TLS_SNI="bsl.sentico-sandbox.xyz"
export SENTICORE_FIX_HOST="fix.sentico-sandbox.xyz"
export SENTICORE_FIX_PORT="9878"
export SENTICORE_FIX_TLS_SNI="fix.sentico-sandbox.xyz"

Sandbox credentials are separate: an API key, HMAC credential or session key issued in one environment fails closed in the other, and signed actions carry sandbox chain id 421614 instead of 42161. Read the chain binding from the connectivity bundle on the host you connected to rather than hardcoding either value.

The SDK release that accompanies the sandbox adds an environment preset, so the whole block above collapses to one value:

const client = new SenticoreClient({ environment: "sandbox" });

Explicit per-field URLs still override the preset. The Python and Rust SDKs expose the same switch as SenticoreConfig.for_environment("sandbox") and SenticoreConfig::sandbox().

/api/order-entry/binary is the tested private-beta order-entry alias. The direct compatibility route is /api/v1/mm/orders/batch.bin. The canonical BSL route is /api/v1/bsl/orders/compact; use institutional_agent HMAC auth or a provisioned lane key from the connectivity bundle.

TypeScript​

The install below resolves npm latest (0.3.0). The linked repository examples currently target the unreleased 0.4.0 source; clone the repository and run them from sdks/typescript when following those examples exactly.

npm install @sentico-labs/sdk
import { SenticoreClient } from "@sentico-labs/sdk";

const client = new SenticoreClient({
publicHttpBaseUrl: "https://api.sentico-labs.xyz",
tradingHttpBaseUrl: "https://api.sentico-labs.xyz",
publicWsUrl: "wss://api.sentico-labs.xyz/api/v1/ws/public",
privateWsUrl: "wss://api.sentico-labs.xyz/api/v1/ws/private/{account}",
orderEntryHttpBaseUrl: "https://api.sentico-labs.xyz",
orderEntryBinaryPath: "/api/order-entry/binary",
orderEntryApiKey: process.env.SENTICORE_ORDER_ENTRY_API_KEY,
bearerToken: process.env.SENTICORE_BEARER_TOKEN,
machineAuth: process.env.SENTICORE_API_KEY_ID
? {
apiKeyId: process.env.SENTICORE_API_KEY_ID,
apiSecret: process.env.SENTICORE_API_SECRET!,
apiPassphrase: process.env.SENTICORE_API_PASSPHRASE!
}
: undefined
});

const markets = await client.public.listMarkets();
const book = await client.public.getMarketOrderbook(1, { book: "YES", depth: 20 });

console.log(markets.data, book.requestId);

Create the client once per process or strategy shard and reuse it. The default Node fetch keeps an internal connection pool; custom fetch implementations should use a persistent agent or dispatcher. A quote loop must not build a new SDK client or HTTP transport per order.

Capabilities​

NamespaceUse
client.publicAnonymous market data reads.
client.tradingPrivate reads and signed/delegated trading writes.
client.orderEntryBSL compact HTTP action-batch submission.
client.mmDeprecated alias for client.orderEntry.
client.rawEscape hatch for endpoints not promoted to stable helpers.
client.wsPublic and private WebSocket helpers.

The SDKs also expose BSL Direct TCP and FIX helper functions for institutional onboarding:

HelperUse
encodeBslTcpHandshakeHelloBuild the 48-byte Direct TCP HandshakeHello.
decodeBslTcpHandshakeAckDecode the 48-byte Direct TCP HandshakeAck.
encodeBslOrderFromSignedActionBuild executable BSL Direct TCP order messages from a signed action and connectivity-bundle routing context.
sessionKeyShadow optionAttach optional Phase-3 session-key shadow auth to wallet-signed Direct TCP orders without changing apply behavior.
createSessionKeyCreate a local Ed25519 session key for the BSL Direct TCP hot path.
sessionDelegationTypedDataBuild the EIP-712 one-time delegation for a session key.
signSessionDelegationEip712Sign the EIP-712 session delegation with a local private key for agent/test flows.
client.orderEntry.registerSessionKeyRegister a wallet-authorized BSL session key.
encodeBslSessionOrderBuild executable BSL Direct TCP messages using AuthSidecarV2 and Ed25519 session-key signing; pass dmsEpoch to opt the order into the dead-man switch (compact-frame flag bit 4, epoch in bytes 20–23).
client.trading.controlDms / controlBslDmsSigned dead-man-switch controls (arm / heartbeat / status / disarm); the session-key policy must allow "cancel".
client.trading.issuePrivateWsTokenPOST /api/v1/ws/token for the private WebSocket; the credential needs read scope.
client.public.getMarketOracleV2 / getOracleFeedV2Public Oracle V2 reads on /api/v2/public/...; 404 for markets without a V2 profile.
encodeBslTcpCompactActionFrameAdd the 8-byte TCP message header around a 192-byte compact frame.
client.orderEntry.getActionChainBindingFetch the production v2 action-signing domain once at startup.
buildMachineAuthHeadersV2Build SC-* HMAC headers for custom transports.
deriveFixSenderCompIdBuild stable SenderCompID(49) from account, agent id, and scope.
buildFixLogonFieldsBuild FIX 4.4 Logon fields with 553/554 HMAC credentials.
encodeFixFrameAdd 8, 9, and 10 framing around ordered FIX fields.
FixpSessionBuild FIXP Negotiate/Establish/Sequence frames and classify inbound frames.
client.orderEntry.getFixpSbeSchemaFetch the published SBE XML schema.
encodeFixpNewOrderSingleEncode an SBE/SOFH NewOrderSingle for FIXP order entry.
encodeFixpOrderCancelRequestEncode FIXP cancel by OrderID or OrigClOrdID.
encodeFixpOrderCancelReplaceRequestEncode FIXP atomic cancel/replace.
encodeFixpOrderMassCancelRequestEncode FIXP account/market/side mass-cancel.
encodeFixpOrderStatusRequestEncode FIXP read-only order status requests.
encodeFixpRetransmitRequestRequest replay for a missed FIXP server sequence range after gap or reconnect.
decodeFixpFrameDecode SOFH-framed FIXP/SBE control and execution-report messages.

BSL Direct TCP​

Read /api/v1/bsl/connectivity and use bslTcp. The native path is raw TCP/TLS, not HTTPS:

cd sdks/typescript
npm ci
npm run build

SENTICORE_BSL_TCP_HOST=bsl.sentico-labs.xyz \
SENTICORE_BSL_TCP_PORT=9001 \
SENTICORE_BSL_TCP_TLS_SNI=bsl.sentico-labs.xyz \
node dist/examples/bsl-tcp-handshake.js

Use @sentico-labs/[email protected] or newer for live order encoding. The supported Direct TCP production shapes are single place actions and QuoteReplace/SpotQuoteReplace atomic groups. Standalone Cancel and AmendOrder are also supported when you pass routingMarket, the market id of the referenced order. The signed action still only carries order_id; the routing market is a Direct TCP shard-routing hint and is rejected if it conflicts with known order state.

For the lowest local signing cost, use BSL session keys instead of wallet ECDSA per order:

Roll out in this order: first keep encodeBslOrderFromSignedAction as the authoritative path and pass sessionKeyShadow while BSL_SESSION_KEY_AUTH_SHADOW_ENABLED=true; after shadow logs are clean, switch the account to encodeBslSessionOrder. The operator can temporarily set BSL_SESSION_KEY_CANARY_ACCOUNTS during rollout, but general access uses an empty canary list plus an active registered session key.

BSL session-key cancelOnDisconnect is supported for Direct TCP session keys. The SDK policy flag enables a session-key scoped sweep: the gateway reports TCP teardown to the sequencer, and the sequencer cancels only live order ids tracked for that session key.

import {
bslCompactRoutingFromConnectivityBundle,
createSessionKey,
encodeBslSessionOrder,
sessionDelegationTypedData,
signSessionDelegationEip712,
signWalletAdminAuth,
SESSION_DELEGATION_SCHEME_EIP712_V2,
WALLET_ADMIN_ACTION_REGISTER_SESSION_KEY
} from "@sentico-labs/sdk";

const bundle = await client.orderEntry.getConnectivityBundle();
const routing = bslCompactRoutingFromConnectivityBundle(bundle.data);
const sessionKey = createSessionKey();

// Wallet-admin auth for the register call (verified 2026-09-13): the registry
// binds the Sentico app to https://app.sentico-labs.xyz, so appName is "Sentico"
// and the request must carry `Origin: https://app.sentico-labs.xyz`.
// registerSessionKey sends no SC-* machine-auth headers (machineAuth: false);
// adding them yields 403.
const walletAuth = signWalletAdminAuth(
{
domain: "app.sentico-labs.xyz",
appName: "Sentico",
environment: "Arbitrum One",
chainId: 42161,
walletAddress: owner,
nonce: delegationNonce, // at least 8 characters, single-use
issuedAt: Date.now(),
expiresAt: Date.now() + 5 * 60_000 // at most 5 minutes ahead
},
WALLET_ADMIN_ACTION_REGISTER_SESSION_KEY,
ownerPrivateKey
);

const policy = {
allowedMarkets: [3],
// "cancel" is required if you will arm the dead-man switch (DMS_AUTH_REJECTED otherwise).
allowedActions: ["place", "spot_place", "cancel"],
maxOrderQtyLots: 1_000_000,
maxNotionalMicro: 250_000_000,
gatewayId: routing.gatewayId,
cancelOnDisconnect: false
};

const delegation = {
walletAuth,
chainBinding: routing.chainBinding,
accountIdHex: account,
sessionPublicKey: sessionKey.publicKey,
policy,
validFrom: Date.now() - 1000,
expiresAt: Date.now() + 60 * 60_000,
delegationNonce
};

const typedDelegation = sessionDelegationTypedData(delegation);
const delegationSignature =
walletClient
? await walletClient.signTypedData(typedDelegation)
: signSessionDelegationEip712(delegation, ownerPrivateKey);

const registered = await client.orderEntry.registerSessionKey(
{
...delegation,
algorithm: "ed25519",
delegationScheme: SESSION_DELEGATION_SCHEME_EIP712_V2,
delegationSignature
},
{ headers: { Origin: "https://app.sentico-labs.xyz" } }
);

const encoded = encodeBslSessionOrder(payload, {
...routing,
accountIdx: routing.accountIdx!,
sessionId,
gatewaySeq,
sessionPrivateKey: sessionKey.privateKey,
sessionKeyId: sessionKey.sessionKeyId,
sessionSeq,
policyHash: (registered.data as any).policyHash,
dmsEpoch // optional: opt this order into an armed dead-man switch
});

See BSL Session Keys for the exact policy fields, rollout flags, and revocation flow.

BSL Compact HTTP Order Entry​

The SDK encodes:

{"version":1,"actions":[...],"idempotencyKey":"client-batch-1"}

and submits it as:

POST /api/order-entry/binary
Content-Type: application/x-senticore-order-entry-batch
X-BSL-Result-Mode: ack
X-Senticore-Response-Mode: detailed
X-Senticore-Order-Entry-Key: <optional provisioned lane key>

The order-entry key authorizes a dedicated lane and rate tier when assigned. It does not replace the signed action inside the batch.

After an ack response, use the SDK reconciliation helpers instead of a bootstrap polling loop:

const receipts = await client.orderEntry.getActionReceipts(
account,
response.data.seqs,
{ waitMs: 250 }
);

const replay = await client.orderEntry.getAccountExecutions(account, {
fromSeq: response.data.lastSeq ?? response.data.seqs.at(-1)
});

console.log(receipts.data.receipts, replay.data);

FIX Logon Example​

After creating an institutional_agent, read GET /api/v1/bsl/connectivity and use the returned CompIDs. The TypeScript SDK contains a runnable Logon example:

cd sdks/typescript
npm ci
npm run build

SENTICORE_ACCOUNT=0x... \
SENTICORE_AGENT_ID=agt_... \
SENTICORE_API_KEY_ID=spk_... \
SENTICORE_API_SECRET=... \
SENTICORE_API_PASSPHRASE=... \
node dist/examples/fix-logon.js

For code that only needs CompIDs and a Logon frame:

import {
buildFixLogonFields,
deriveFixSenderCompId,
encodeFixFrame
} from "@sentico-labs/sdk";

const senderCompId = deriveFixSenderCompId(account, agentId, "order_entry");
const frame = encodeFixFrame(buildFixLogonFields({
account,
apiKeyId,
apiSecret,
apiPassphrase,
senderCompId,
targetCompId: "SENTICORE",
targetSubId: "order_entry"
}));

FIXP / SBE​

FIXP is the binary-FIX lane on the direct TCP/TLS host. Read GET /api/v1/fixp/connectivity; the SBE XML is published at GET /api/v1/fixp/schema and referenced as fixp.wire.schemaUrl.

import {
FixpSession,
FixpSide,
decodeFixpFrame,
encodeFixpNewOrderSingle
} from "@sentico-labs/sdk";

const session = new FixpSession({
apiKeyId,
apiSecret,
apiPassphrase,
account
});

socket.write(Buffer.concat(session.start()));
socket.write(encodeFixpNewOrderSingle({
clOrdId: "mm-1",
account,
marketId: 1,
price: 1_000_000,
orderQty: 1,
side: FixpSide.Bid
}));

const inbound = decodeFixpFrame(frameFromSocket);

For FIXP reconnect/recovery, create the next FixpSession with the same sessionId and account credentials, complete Negotiate/Establish again, then call session.encodeRetransmitRequest(fromSeqNo, count) for any missed server sequence range. The server resumes only same-account retained sessions.

Python​

The Python SDK is a repository preview (alpha, not yet on a public registry). The example below requires the declared dependencies (for example httpx) to be installed by pip during the editable install.

git clone https://github.com/sentico-labs/senticore.git
cd senticore/sdks/python
python -m pip install -e .
from senticore import SenticoreClient, SenticoreConfig

client = SenticoreClient(
SenticoreConfig(
public_http_base_url="https://api.sentico-labs.xyz",
trading_http_base_url="https://api.sentico-labs.xyz",
public_ws_url="wss://api.sentico-labs.xyz/api/v1/ws/public",
private_ws_url="wss://api.sentico-labs.xyz/api/v1/ws/private/{account}",
order_entry_http_base_url="https://api.sentico-labs.xyz",
order_entry_binary_path="/api/order-entry/binary",
order_entry_api_key="...",
)
)

Rust​

[dependencies]
senticore-sdk = { path = "sdks/rust" }
use senticore_sdk::{SenticoreClient, SenticoreConfig};

let client = SenticoreClient::new(
SenticoreConfig::new("https://api.sentico-labs.xyz", "https://api.sentico-labs.xyz")
.with_order_entry_api_key("optional-lane-key"),
)?;

Market Maker Verification​

Use the repository audit script before handing SDK config to a market maker:

node scripts/e2e/mm-docs-contract-audit.cjs

See Market Maker Beta Quickstart.