Submitting orders
Order Entry is the professional/institutional submission surface. It accepts a batch of signed actions and routes them into the same sequencer, risk checks, matching engine, execution stream, and recovery model as every other lane. Two transports carry the same batch:
| Endpoint | Body | Use |
|---|---|---|
POST /api/v1/order-entry/orders | JSON | App backends, bots, debuggable submission |
POST /api/v1/order-entry/orders/compact | Byte body | Market makers, quote engines, lower-overhead batches |
Both submit the identical signed-action batch and return the same acknowledgement model. The byte lane is not a separate matching engine and is not FIX — it is an HTTP POST with a byte payload. For how to choose a lane, see Order Entry Lanes. For connectivity, hosts, credentials, and the exact signing scheme, see Authentication and Action Signing.
The names "MM", "BSL", and "binary" are legacy aliases for Order Entry. The
following routes remain live for already-wired clients but should not be used by
new integrations: POST /api/v1/bsl/orders/batch,
POST /api/v1/bsl/orders/compact, POST /api/order-entry/binary,
POST /api/v1/mm/orders/batch.bin, and the /api/v1/bsl/orders/replace,
/api/v1/bsl/orders/cancel-replace, /api/v1/mm/orders/replace,
/api/v1/mm/orders/cancel-replace facades.
Signed-action batch shape
The batch wraps an array of signed action envelopes. Each envelope is a
canonical action payload plus its signature. The same shape is used by both
the JSON and compact endpoints; the compact endpoint sends it as bytes.
{
"version": 1,
"actions": [
{
"payload": {
"account": "0x1111111111111111111111111111111111111111",
"nonce": 42,
"ts": 1781190000000,
"action": {
"SpotPlaceOrder": {
"market": 7,
"side": "Bid",
"price": 998400,
"qty": 1000,
"timeInForce": "post_only"
}
}
},
"signature": {
"scheme": "EcdsaSecp256k1",
"bytes": "0x..."
}
}
],
"idempotencyKey": "batch-9001"
}
The account signature is the primary authority for every action. The byte lane
does not replace action signing. clientOrderId, when used, must be inside
the signed action payload so the engine can reconcile by client id
deterministically.
Supported actions
The endpoint accepts signed action envelopes after the same public action validation as the rest of the API:
| Action | Use |
|---|---|
SpotPlaceOrder | Single spot order |
OutcomePlaceOrder / PlaceOrder | Single prediction-market order |
Cancel | Cancel by order id |
AmendOrder | Quantity reduction while keeping priority |
SpotQuoteReplace | Spot quote refresh |
QuoteReplace | Prediction-market quote refresh |
PlaceConditionalOrder | Conditional order where enabled |
PlaceAlgoOrder | Algo order where enabled |
Internal actions — deposit credits, settlement credits, market creation, and resolution — are rejected on the Order Entry path.
Result mode
Send X-BSL-Result-Mode (the SDK responseMode option maps to this header) to
choose how much of the engine result is returned inline:
| Value | Meaning |
|---|---|
ack | Sequencer accepted the request boundary. Lowest response overhead. |
durable | Durable/WAL boundary reached. |
full | Engine-applied result included when available. |
An ack confirms the configured ingress boundary, not final fill state. Use
full when the strategy needs the engine-applied result in the HTTP response;
use ack for high-rate flows that reconcile from the private execution stream.
JSON batch
POST /api/v1/order-entry/orders
Content-Type: application/json
X-BSL-Result-Mode: full
Idempotency-Key: strategy-42-batch-9001
{
"version": 1,
"actions": [
{
"payload": { "...": "signed ActionPayload" },
"signature": { "scheme": "EcdsaSecp256k1", "bytes": "0x..." }
}
],
"idempotencyKey": "strategy-42-batch-9001"
}
Use the JSON batch when a debuggable request body matters more than overhead.
Compact/binary batch
POST /api/v1/order-entry/orders/compact
Content-Type: application/x-senticore-order-entry-batch
Accept: application/x-senticore-order-entry-batch-response, application/json
X-BSL-Result-Mode: full
X-Senticore-Order-Entry-Key: <order-entry-lane-key>
Idempotency-Key: quote-refresh-1
The compact lane is for low-overhead submission and deterministic acknowledgement. The body is the byte-encoded order-entry batch.
If operations has assigned a dedicated lane key, include it as
X-Senticore-Order-Entry-Key. For a builder rotate-key credential, the
lane-key value is credential.apiKeyId — never put apiSecret or
apiPassphrase in this header. The lane key is not a public shared secret; some
signed-submit paths allow a missing lane key during private beta, but do not
rely on that for production. The legacy X-MM-Key header is accepted only as a
compatibility alias.
Byte framing and encoding
The compact submission is an HTTP POST body. It is not a FIX session and it does not use client-managed TCP sequence frames; FIX is documented separately because it needs a provisioned TCP/TLS endpoint, CompIDs, Logon credentials, and sequence recovery (see FIX).
Accepted content types:
| Content type | Status |
|---|---|
application/x-senticore-order-entry-batch | Preferred |
application/x-senticore-mm-batch | Legacy compatibility |
application/x-senticore-binary | Compatibility |
application/octet-stream | Compatibility |
A request without a content type is treated as a binary/byte submission by the server-side decoder.
Encoding. The TypeScript, Python, and Rust SDKs encode the batch JSON as UTF-8 bytes and submit it with the preferred content type:
{"version":1,"actions":[...],"idempotencyKey":"client-batch-1"}
The server first tries its internal cold binary decoder, then falls back to this SDK JSON byte shape.
Limits. Read GET /api/v1/bsl/limits (legacy alias GET /api/v1/mm/limits)
rather than hard-coding limits. The response reports max actions per batch,
low-latency max actions, max binary body bytes, timestamp skew, rate windows,
and backlog limits.
Windowed nonces for concurrency
Nonces use a windowed replay-protection model: any unused nonce in
[nonceFloor, nonceFloor + nonceWindow) (currently nonceWindow = 256) is
accepted. Gaps and out-of-order are allowed — a batch's legs and concurrent
batches may use arbitrary unused in-window nonces in any order. You do not
need contiguous nonces, gap-filling, ack round-trips, or nonce reservations; the
old "nonce gap" rejection no longer exists.
Per-leg rejects are independent and carry nonceFloor / nonceWindow plus a
code (nonce_below_floor / nonce_outside_window / nonce_replayed).
Re-pick a fresh in-window nonce for the rejected leg only. See
Order Concurrency & Nonces for the full model.
Cancel-replace
Cancel-replace amends price, quantity, or order flags without manually issuing separate cancel and place calls. Separate operations can expose a strategy to stale-quote risk or temporary loss of queue position; cancel-replace gives one operation with one acknowledgement path.
For new integrations, use a signed QuoteReplace (prediction markets) or
SpotQuoteReplace (spot) action inside the batch. A leg's cancel_order_id
removes a stale quote leg in the same signed action:
- A leg with
cancel_order_idandqty: 0is cancel-only. - A leg with
cancel_order_idand a non-zeroqtycancels and re-places. - A leg without
cancel_order_idis place-only.
{
"version": 1,
"actions": [
{
"payload": {
"account": "0x1111111111111111111111111111111111111111",
"nonce": 42,
"ts": 1781190000000,
"action": {
"SpotQuoteReplace": {
"market": 1,
"legs": [
{
"cancel_order_id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"side": "Bid",
"price": 999900,
"qty": 100000,
"time_in_force": "post_only"
}
]
}
}
},
"signature": { "scheme": "EcdsaSecp256k1", "bytes": [1, 2, 3] }
}
],
"idempotencyKey": "replace-bid-1"
}
Replaceable fields
| Field | Replaceable | Notes |
|---|---|---|
| Price | Yes | Must satisfy tick size |
| Quantity | Yes | Must satisfy lot size and collateral checks |
| Time-in-force | Limited | Some transitions require cancel and new order |
| Side | No | Use cancel and new order |
| Market | No | Use cancel and new order |
| Account | No | Use cancel and new order |
Failure behavior
Replace is one result boundary. Do not infer that the original order is gone from
request acceptance alone — if the replacement is rejected, the original order
remains active unless the response explicitly says it was cancelled. In full
mode reconcile the order state from the engine result:
| Status | Client action |
|---|---|
filled | Position and exposure changed immediately. |
partially_filled | Continue quoting with updated leaves quantity. |
resting | New order is live on the book. |
canceled | Cancel side completed. |
rejected | Inspect rejectCode; do not assume the old order is gone unless the result says so. |
The FIX equivalent is OrderCancelReplaceRequest (35=G); see
FIX Order Entry.
Bulk operations
Bulk operations let liquidity providers and HFT quote engines update many quotes
with one request. Submit a batch of signed actions exactly as above —
QuoteReplace and SpotQuoteReplace are the preferred quote-refresh actions —
and rely on windowed nonces to pipeline concurrent batches.
Atomicity is action-level, not a separate matching engine:
- Frame, auth, timestamp, and schema failures reject the whole batch before enqueue.
- Accepted actions enter the normal sequencer path; per-action rejects are independent.
- Terminal state is reconciled from receipts, private streams, or account reads.
Cancel by clientOrderId, mass-cancel per session, and cancel-on-disconnect
should use the same session and private stream for reconciliation. For
high-frequency quoting, do not treat HTTP 200 on a non-full request as a
terminal order state — ack means the request boundary was accepted; the
execution stream or gap-fill is the source for fills, cancels, and rejects.
Acknowledgement and timing
The response contains ok, accepted sequence ids (seqs), derived order ids,
result mode, ACK mode, rate-limit metadata, timing headers, and optional per-order
actionResults depending on the response mode.
Every response includes timing fields so clients can locate latency:
| Field | Meaning |
|---|---|
gatewayReceivedTs | Server ingress timestamp. |
authDoneTs | Auth and credential gate completed. |
riskDoneTs | Risk or credit approval completed when applicable. |
sequencedTs | Request entered sequencing. |
engineAppliedTs | Engine terminal event timestamp when available. |
durableTs | Durable acknowledgement timestamp when requested and reached. |
responseSentTs | Response emission timestamp. |
The durationsUs object carries microsecond breakdowns for auth, routing, risk,
enqueue, ack resolution, response emission, and total server time.
Idempotency
Use both the HTTP Idempotency-Key header and the body idempotencyKey field
for retriable batches. The server hashes the body and response mode; retrying the
same key returns the existing response when the request hash matches. Retrying a
key with a different body is a client error — treat it as a client bug and
reconcile before submitting new risk. Idempotency-Key is the HTTP retry-dedup
key; clientOrderId is the strategy correlation id. See
Idempotency.
Failure model
Rejects use stable numeric reason codes that map to the public
Error Model: tick-size violation, insufficient
collateral, stale nonce, duplicate client order id, and account-permission
failures. On SHARD_BUSY or QUEUE_LIMIT, back off or reroute quoting for that
market. During private beta some rejection paths still return string messages
while machine-readable codes are expanded; preserve the raw error body in logs.
Client rules
- Use
ackfor high-rate quote refreshes that reconcile from the stream. - Use
fullfor IOC/FOK, cancel, and replace. - Use
durableonly when persistence acknowledgement matters more than latency. - Treat
Idempotency-Keyas the retry-dedup key andclientOrderIdas the strategy correlation id. - The durable truth is the sequencer/execution stream — the first HTTP acknowledgement is not the final order state.