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
- Create an
institutional_agentthrough the wallet-authorized API Agent flow. - Request an HMAC credential for that agent.
- Read the connectivity bundle with the HMAC credential.
- Cache
actionSigning.chainId,actionSigning.verifyingContract, compact mapping version, BSL TCP route, and FIX CompIDs in your OMS/quote engine. - For the standard BSL Direct TCP path, sign every order action locally and
send a wallet-signed
AuthSidecarV1with each hot frame. - For the lower-latency session-key path, register a short-lived Ed25519
session key and send
AuthSidecarV2frames withencodeBslSessionOrder. - 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
| Field | FIX tag | Value |
|---|---|---|
SenderCompID | 49 | Client-stable id. Use bundle value such as SC-ABCDEF12-123456-OE. |
TargetCompID | 56 | SENTICORE unless the bundle says otherwise. |
TargetSubID | 57 | order_entry or drop_copy. |
Account | 1 | Engine account address. |
Username | 553 | institutional_agent.apiKeyId. |
Password | 554 | <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
| Surface | Endpoint rule |
|---|---|
| Public/Retail HTTP | https://api.sentico-labs.xyz through Cloudflare. |
| BSL Direct TCP | Use bslTcp.host, bslTcp.port, and bslTcp.tlsSni from the bundle. This is the native non-Cloudflare order-entry path. |
| BSL direct HTTP | Use directHttpBaseUrl when assigned. This is compatibility/admin-friendly BSL HTTP, not the native TCP path. |
| FIX | Raw TCP/TLS from the bundle host, port, and tlsSni. It is not an HTTPS route. |
| FIXP/SBE | Raw 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_agentcredentials are for normal HTTP bots and private reads.- BSL/FIX order entry uses
institutional_agentHMAC credentials. - The bundle does not return
apiSecretorapiPassphrase; 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=truefor first-account shadow checks, then enable realAuthSidecarV2withBSL_SESSION_KEY_AUTH_ENABLED=true. LeaveBSL_SESSION_KEY_CANARY_ACCOUNTSempty for general access by registered session-key accounts, or set it only as a temporary rollout allowlist.