Skip to main content

FIX/BSL Connectivity Bundle

Market makers should start from the connectivity bundle instead of guessing FIX CompIDs, route aliases, or Cloudflare behavior.

GET /api/v1/bsl/connectivity
GET /api/v1/fix/connectivity

Both routes return the same contract. Without authentication, the response describes public endpoint rules and derivation formulas. With an institutional_agent HMAC request, the response also includes the account, agent id, API key id, scopes, and concrete recommended SenderCompID values.

Self-service flow

  1. Create an institutional_agent through the wallet-authorized API Agent flow.
  2. Request an HMAC credential for that agent.
  3. Read the connectivity bundle with the HMAC credential.
  4. Cache actionSigning.chainId, actionSigning.verifyingContract, compact mapping version, BSL TCP route, and FIX CompIDs in your OMS/quote engine.
  5. For the standard BSL Direct TCP path, sign every order action locally and send a wallet-signed AuthSidecarV1 with each hot frame.
  6. For the lower-latency session-key path, register a short-lived Ed25519 session key and send AuthSidecarV2 frames with encodeBslSessionOrder.
  7. Keep the HMAC credential for onboarding, private reads, FIX logon, and control-plane calls; it does not replace per-order authorization.

No manual operations ticket is required just to choose SenderCompID(49) or TargetCompID(56).

FIX CompIDs

FieldFIX tagValue
SenderCompID49Client-stable id. Use bundle value such as SC-ABCDEF12-123456-OE.
TargetCompID56SENTICORE unless the bundle says otherwise.
TargetSubID57order_entry or drop_copy.
Account1Engine account address.
Username553institutional_agent.apiKeyId.
Password554<apiSecret>:<apiPassphrase>.

The FIX session key is:

SenderCompID + TargetCompID + Account

Reuse the same SenderCompID across reconnects so sequence recovery works. Use a separate suffix for drop-copy, for example -DC, when you want order entry and drop-copy to maintain independent sequence state.

Direct endpoints

SurfaceEndpoint rule
Public/Retail HTTPhttps://api.sentico-labs.xyz through Cloudflare.
BSL Direct TCPUse bslTcp.host, bslTcp.port, and bslTcp.tlsSni from the bundle. This is the native non-Cloudflare order-entry path.
BSL direct HTTPUse directHttpBaseUrl when assigned. This is compatibility/admin-friendly BSL HTTP, not the native TCP path.
FIXRaw TCP/TLS from the bundle host, port, and tlsSni. It is not an HTTPS route.
FIXP/SBERaw TCP/TLS from fixp.host, fixp.port, and fixp.tlsSni when fixp.enabled=true.

Current production BSL Direct TCP examples use a DNS-only hostname with a public CA certificate:

host: bsl.sentico-labs.xyz
port: 9001
tlsSni: bsl.sentico-labs.xyz
protocol: senticore-bsl-tcp-v2

Current production FIX examples use a separate DNS-only hostname with a public CA certificate:

host: fix.sentico-labs.xyz
port: 9878
tlsSni: fix.sentico-labs.xyz
targetCompId: SENTICORE

Both fix.sentico-labs.xyz and bsl.sentico-labs.xyz must remain Cloudflare DNS-only records. Do not orange-proxy them: Cloudflare HTTPS does not carry raw BSL TCP or FIX/TCP.

BSL Direct TCP

The native BSL TCP protocol is:

transport: tcp_tls
protocol: senticore-bsl-tcp-v2
handshake: 48 bytes
message header: u32_le kind + u32_le payload_len
compact action frame: 192 bytes
response envelope: SequencedData(kind=17) with u64_le session_seq + u32_le inner_kind

Use a persistent session and set TCP_NODELAY. This is the path to use for latency-sensitive quote engines. See BSL Direct TCP for the full frame contract and SDK handshake examples.

BSL auth sidecar in practice

BSL Direct TCP is not "TLS login, then unsigned frames". The TCP session is only the recoverable transport. Every external order still needs signed action material:

single order:
AuthSidecar(kind=4, payload=cold AuthSidecar)
CompactActionFrame(kind=1, payload=192-byte frame)

atomic group:
CompactFrameGroup(kind=10, payload=cold CompactFrameGroup)
# every group item contains frame_bytes + sidecar

The legacy wallet-signed AuthSidecarV1 payload is a cold-encoded internal object, not JSON:

auth_binding = blake3(signature || nonce_le_u64 || action_hash_v2)
signature = secp256k1 signature from SignedAction
signer_id = raw 20-byte authorized EVM signer address
payload_bytes = cold-encoded SignedAction, required for V1

action_hash_v2 must use the bundle's actionSigning.chainId and actionSigning.verifyingContract. The compact frame must copy the same auth_binding. The sequencer rejects missing sidecars, binding drift, wrong signers, stale compact mappings, nonce reuse, and attempts to switch account or signer identity on an established TCP session.

The TypeScript SDK has a high-level Direct TCP encoder for production order messages:

import {
BslTcpSession,
bslCompactRoutingFromConnectivityBundle,
encodeBslOrderFromSignedAction,
encodeBslTcpAuthSidecar,
encodeBslTcpCompactActionFrame,
encodeBslTcpCompactFrameGroup
} from "@sentico-labs/sdk";

const routing = bslCompactRoutingFromConnectivityBundle(connectivityBundle);

socket.write(session.encodeHello(1000));
// read 48-byte HandshakeAck, then build and write the order messages:

const encoded = encodeBslOrderFromSignedAction(signedAction, {
...routing,
accountIdx: routing.accountIdx!,
sessionId,
gatewaySeq
});
for (const message of encoded.tcpMessages) socket.write(message);

// Low-level diagnostics only:
socket.write(encodeBslTcpAuthSidecar(sidecarBytes));
socket.write(encodeBslTcpCompactActionFrame(frameBytes));

encodeBslTcpAuthSidecar, encodeBslTcpCompactActionFrame, and encodeBslTcpCompactFrameGroup only write the kind,len header around already prepared bytes. They do not sign the action and they do not turn JSON into a valid sidecar or frame.

Use @sentico-labs/sdk@0.1.4 or newer. Direct TCP production order entry supports single place actions and QuoteReplace/SpotQuoteReplace groups. Standalone Cancel and AmendOrder are supported when the client passes routingMarket, the market id of the referenced order, so the compact gateway can route the frame before apply. The sequencer treats that value as a routing hint and rejects known mismatches.

Session-key Direct TCP

Session keys are the low-local-latency variant for quote engines. The owner wallet signs one EIP-712 SessionDelegationV2 bound to the bundle's actionSigning.chainId and actionSigning.verifyingContract. The hot path then signs each compact frame with an Ed25519 session key and sends AuthSidecarV2:

auth_scheme = senticore_session_key_v1
algorithm = ed25519
session_key_id = blake3("SENTICORE/SESSION_KEY_ID/v1" || public_key)
session_seq = monotone u64 per session key
policy_hash = blake3("SENTICORE/SESSION_POLICY/v1" || policy_json)
order_hash = chain-bound compact frame hash with auth_binding zeroed
signature = ed25519_sign(order_hash)
payload_bytes = null

Use the SDK helpers instead of constructing these fields by hand:

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

See BSL Session Keys for the full lifecycle and rollout flags.

BSL submit

The tested beta submit route remains:

POST /api/order-entry/binary
Content-Type: application/x-senticore-order-entry-batch
Accept: application/x-senticore-order-entry-batch-response, application/json
X-BSL-Result-Mode: ack
X-Senticore-Response-Mode: detailed

The canonical route is:

POST /api/v1/bsl/orders/compact

Use it with institutional_agent HMAC auth or a provisioned lane key from the connectivity bundle.

SDK examples

BSL Direct TCP handshake:

cd sdks/typescript
SENTICORE_BSL_TCP_HOST=bsl.sentico-labs.xyz \
SENTICORE_BSL_TCP_PORT=9001 \
SENTICORE_BSL_TCP_TLS_SNI=bsl.sentico-labs.xyz \
npm run build && node dist/examples/bsl-tcp-handshake.js
cd sdks/python
python -m pip install -e .
SENTICORE_BSL_TCP_HOST=bsl.sentico-labs.xyz \
SENTICORE_BSL_TCP_PORT=9001 \
SENTICORE_BSL_TCP_TLS_SNI=bsl.sentico-labs.xyz \
python examples/bsl_tcp_handshake.py

TypeScript:

cd sdks/typescript
SENTICORE_ACCOUNT=0x... \
SENTICORE_AGENT_ID=agt_... \
SENTICORE_API_KEY_ID=spk_... \
SENTICORE_API_SECRET=... \
SENTICORE_API_PASSPHRASE=... \
npm run build && node dist/examples/fix-logon.js

Python:

cd sdks/python
python -m pip install -e .
SENTICORE_ACCOUNT=0x... \
SENTICORE_AGENT_ID=agt_... \
SENTICORE_API_KEY_ID=spk_... \
SENTICORE_API_SECRET=... \
SENTICORE_API_PASSPHRASE=... \
python examples/fix_logon.py

Certificate verification is on by default. SENTICORE_FIX_INSECURE_TLS=1 is only for local lab endpoints and must not be used for production connectivity.

Example bundle

{
"contract": "SENTICORE_INSTITUTIONAL_CONNECTIVITY_V1",
"auth": {
"requiredAgentType": "institutional_agent",
"credentialKind": "hmac",
"fixUsernameTag553": "apiKeyId",
"fixPasswordTag554": "<apiSecret>:<apiPassphrase>"
},
"actionSigning": {
"domain": "SENTICORE/ACTION_PAYLOAD/v2",
"version": 2,
"chainId": 42161,
"verifyingContract": "0x..."
},
"bsl": {
"directHttpBaseUrl": "https://api.sentico-labs.xyz",
"testedBetaSubmitPath": "/api/order-entry/binary",
"canonicalSubmitPath": "/api/v1/bsl/orders/compact"
},
"bslTcp": {
"enabled": true,
"transport": "tcp_tls",
"host": "bsl.sentico-labs.xyz",
"port": 9001,
"tlsSni": "bsl.sentico-labs.xyz",
"protocol": "senticore-bsl-tcp-v2",
"handshakeBytes": 48,
"messageHeaderBytes": 8,
"compactActionFrameBytes": 192,
"frameVersion": 1,
"orderEncoding": {
"sdkFunction": "encodeBslOrderFromSignedAction",
"sessionKeySdkFunction": "encodeBslSessionOrder",
"authSidecarVersions": {
"v1": {
"scheme": "signed_action_payload",
"algorithm": "ecdsa_secp256k1",
"payloadBytes": "required"
},
"v2": {
"scheme": "senticore_session_key_v1",
"algorithm": "ed25519",
"payloadBytes": null
}
}
}
},
"fix": {
"host": "fix.sentico-labs.xyz",
"port": 9878,
"tlsSni": "fix.sentico-labs.xyz",
"targetCompIdTag56": "SENTICORE",
"orderEntry": {
"targetSubIdTag57": "order_entry",
"senderCompIdTag49": "SC-ABCDEF12-123456-OE"
}
},
"fixp": {
"host": "fixp.sentico-labs.xyz",
"port": 9879,
"tlsSni": "fixp.sentico-labs.xyz",
"protocol": "senticore-fixp-sbe-v1",
"wire": {
"framing": "SOFH",
"encoding": "SBE little-endian",
"schemaId": 1,
"schemaVersion": 1,
"schemaUrl": "/api/v1/fixp/schema",
"sessionFlow": "recoverable"
},
"admission": "session_auth"
}
}

Guardrails

  • api_agent credentials are for normal HTTP bots and private reads.
  • BSL/FIX order entry uses institutional_agent HMAC credentials.
  • The bundle does not return apiSecret or apiPassphrase; those are shown only when the credential is created.
  • A dedicated BSL lane key is optional and only controls a dedicated rate tier. It does not replace signed actions or institutional HMAC auth.
  • Session-key Direct TCP is feature-flagged server-side. Use BSL_SESSION_KEY_AUTH_SHADOW_ENABLED=true for first-account shadow checks, then enable real AuthSidecarV2 with BSL_SESSION_KEY_AUTH_ENABLED=true. Leave BSL_SESSION_KEY_CANARY_ACCOUNTS empty for general access by registered session-key accounts, or set it only as a temporary rollout allowlist.

Next